VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.3 KB
Line 
1/* $Id: DevVirtioNet.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DevVirtioNet - Virtio Network Device
4 */
5
6/*
7 * Copyright (C) 2009-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
33#define VNET_WITH_GSO
34#define VNET_WITH_MERGEABLE_RX_BUFS
35
36#include <VBox/vmm/pdmdev.h>
37#include <VBox/vmm/pdmnetifs.h>
38#include <iprt/asm.h>
39#include <iprt/net.h>
40#include <iprt/semaphore.h>
41#include <iprt/string.h>
42#ifdef IN_RING3
43# include <iprt/uuid.h>
44#endif
45#include <VBox/VBoxPktDmp.h>
46#include "VBoxDD.h"
47#include "../VirtIO/Virtio.h"
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53#ifndef VBOX_DEVICE_STRUCT_TESTCASE
54
55#define INSTANCE(pThis) pThis->VPCI.szInstance
56
57#ifdef IN_RING3
58
59# define VNET_PCI_CLASS 0x0200
60# define VNET_N_QUEUES 3
61
62# if 0
63/* Virtio Block Device */
64# define VNET_PCI_CLASS 0x0180
65# define VNET_N_QUEUES 2
66# endif
67
68#endif /* IN_RING3 */
69
70#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
71
72/*
73 * Commenting out VNET_TX_DELAY enables async transmission in a dedicated thread.
74 * When VNET_TX_DELAY is defined, a timer handler does the job.
75 */
76//#define VNET_TX_DELAY 150 /**< 150 microseconds */
77#define VNET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP packet size + Ethernet header with VLAN tag */
78#define VNET_MAC_FILTER_LEN 32
79#define VNET_MAX_VID (1 << 12)
80
81/** @name Virtio net features
82 * @{ */
83#define VNET_F_CSUM 0x00000001 /**< Host handles pkts w/ partial csum */
84#define VNET_F_GUEST_CSUM 0x00000002 /**< Guest handles pkts w/ partial csum */
85#define VNET_F_MAC 0x00000020 /**< Host has given MAC address. */
86#define VNET_F_GSO 0x00000040 /**< Host handles pkts w/ any GSO type */
87#define VNET_F_GUEST_TSO4 0x00000080 /**< Guest can handle TSOv4 in. */
88#define VNET_F_GUEST_TSO6 0x00000100 /**< Guest can handle TSOv6 in. */
89#define VNET_F_GUEST_ECN 0x00000200 /**< Guest can handle TSO[6] w/ ECN in. */
90#define VNET_F_GUEST_UFO 0x00000400 /**< Guest can handle UFO in. */
91#define VNET_F_HOST_TSO4 0x00000800 /**< Host can handle TSOv4 in. */
92#define VNET_F_HOST_TSO6 0x00001000 /**< Host can handle TSOv6 in. */
93#define VNET_F_HOST_ECN 0x00002000 /**< Host can handle TSO[6] w/ ECN in. */
94#define VNET_F_HOST_UFO 0x00004000 /**< Host can handle UFO in. */
95#define VNET_F_MRG_RXBUF 0x00008000 /**< Host can merge receive buffers. */
96#define VNET_F_STATUS 0x00010000 /**< virtio_net_config.status available */
97#define VNET_F_CTRL_VQ 0x00020000 /**< Control channel available */
98#define VNET_F_CTRL_RX 0x00040000 /**< Control channel RX mode support */
99#define VNET_F_CTRL_VLAN 0x00080000 /**< Control channel VLAN filtering */
100/** @} */
101
102#define VNET_S_LINK_UP 1
103
104
105/*********************************************************************************************************************************
106* Structures and Typedefs *
107*********************************************************************************************************************************/
108#ifdef _MSC_VER
109struct VNetPCIConfig
110#else /* !_MSC_VER */
111struct __attribute__ ((__packed__)) VNetPCIConfig /** @todo r=bird: Use #pragma pack if necessary, that's portable! */
112#endif /* !_MSC_VER */
113{
114 RTMAC mac;
115 uint16_t uStatus;
116};
117AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
118
119/**
120 * The virtio-net shared instance data.
121 *
122 * @extends VPCISTATE
123 */
124typedef struct VNETSTATE
125{
126 VPCISTATE VPCI;
127
128// PDMCRITSECT csRx; /**< Protects RX queue. */
129
130#ifdef VNET_TX_DELAY
131 /** Transmit Delay Timer. */
132 TMTIMERHANDLE hTxTimer;
133 uint32_t u32i;
134 uint32_t u32AvgDiff;
135 uint32_t u32MinDiff;
136 uint32_t u32MaxDiff;
137 uint64_t u64NanoTS;
138#else /* !VNET_TX_DELAY */
139 /** The event semaphore TX thread waits on. */
140 SUPSEMEVENT hTxEvent;
141#endif /* !VNET_TX_DELAY */
142
143 /** Indicates transmission in progress -- only one thread is allowed. */
144 uint32_t uIsTransmitting;
145
146 /** PCI config area holding MAC address as well as TBD. */
147 struct VNetPCIConfig config;
148 /** MAC address obtained from the configuration. */
149 RTMAC macConfigured;
150 /** True if physical cable is attached in configuration. */
151 bool fCableConnected;
152 /** Link up delay (in milliseconds). */
153 uint32_t cMsLinkUpDelay;
154
155 uint32_t alignment;
156
157 /** Number of packet being sent/received to show in debug log. */
158 uint32_t u32PktNo;
159
160 /** N/A: */
161 bool volatile fMaybeOutOfSpace;
162
163 /** Promiscuous mode -- RX filter accepts all packets. */
164 bool fPromiscuous;
165 /** AllMulti mode -- RX filter accepts all multicast packets. */
166 bool fAllMulti;
167 /** The number of actually used slots in aMacTable. */
168 uint32_t cMacFilterEntries;
169 /** Array of MAC addresses accepted by RX filter. */
170 RTMAC aMacFilter[VNET_MAC_FILTER_LEN];
171 /** Bit array of VLAN filter, one bit per VLAN ID. */
172 uint8_t aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
173
174 /* Receive-blocking-related fields ***************************************/
175
176 /** EMT: Gets signalled when more RX descriptors become available. */
177 SUPSEMEVENT hEventMoreRxDescAvail;
178
179 /** Handle of the I/O port range. */
180 IOMIOPORTHANDLE hIoPorts;
181
182 /** @name Statistic
183 * @{ */
184 STAMCOUNTER StatReceiveBytes;
185 STAMCOUNTER StatTransmitBytes;
186 STAMCOUNTER StatReceiveGSO;
187 STAMCOUNTER StatTransmitPackets;
188 STAMCOUNTER StatTransmitGSO;
189 STAMCOUNTER StatTransmitCSum;
190#ifdef VBOX_WITH_STATISTICS
191 STAMPROFILE StatReceive;
192 STAMPROFILE StatReceiveStore;
193 STAMPROFILEADV StatTransmit;
194 STAMPROFILE StatTransmitSend;
195 STAMPROFILE StatRxOverflow;
196 STAMCOUNTER StatRxOverflowWakeup;
197 STAMCOUNTER StatTransmitByNetwork;
198 STAMCOUNTER StatTransmitByThread;
199#endif
200 /** @} */
201} VNETSTATE;
202/** Pointer to the virtio-net shared instance data. */
203typedef VNETSTATE *PVNETSTATE;
204
205
206/**
207 * The virtio-net ring-3 instance data.
208 *
209 * @extends VPCISTATER3
210 * @implements PDMINETWORKDOWN
211 * @implements PDMINETWORKCONFIG
212 */
213typedef struct VNETSTATER3
214{
215 VPCISTATER3 VPCI;
216
217 PDMINETWORKDOWN INetworkDown;
218 PDMINETWORKCONFIG INetworkConfig;
219 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
220 R3PTRTYPE(PPDMINETWORKUP) pDrv; /**< Connector of attached network driver. */
221 /** The device instance.
222 * @note This is _only_ for use when dealing with interface callbacks. */
223 PPDMDEVINSR3 pDevIns;
224
225#ifndef VNET_TX_DELAY
226 R3PTRTYPE(PPDMTHREAD) pTxThread;
227#endif
228
229 R3PTRTYPE(PVQUEUE) pRxQueue;
230 R3PTRTYPE(PVQUEUE) pTxQueue;
231 R3PTRTYPE(PVQUEUE) pCtlQueue;
232
233 /** Link Up(/Restore) Timer. */
234 TMTIMERHANDLE hLinkUpTimer;
235} VNETSTATER3;
236/** Pointer to the virtio-net ring-3 instance data. */
237typedef VNETSTATER3 *PVNETSTATER3;
238
239
240/**
241 * The virtio-net ring-0 instance data.
242 *
243 * @extends VPCISTATER0
244 */
245typedef struct VNETSTATER0
246{
247 VPCISTATER0 VPCI;
248} VNETSTATER0;
249/** Pointer to the virtio-net ring-0 instance data. */
250typedef VNETSTATER0 *PVNETSTATER0;
251
252
253/**
254 * The virtio-net raw-mode instance data.
255 *
256 * @extends VPCISTATERC
257 */
258typedef struct VNETSTATERC
259{
260 VPCISTATERC VPCI;
261} VNETSTATERC;
262/** Pointer to the virtio-net ring-0 instance data. */
263typedef VNETSTATERC *PVNETSTATERC;
264
265
266/** The virtio-net currenct context instance data. */
267typedef CTX_SUFF(VNETSTATE) VNETSTATECC;
268/** Pointer to the virtio-net currenct context instance data. */
269typedef CTX_SUFF(PVNETSTATE) PVNETSTATECC;
270
271
272
273#ifndef VBOX_DEVICE_STRUCT_TESTCASE
274
275#define VNETHDR_F_NEEDS_CSUM 1 // Use u16CSumStart, u16CSumOffset
276
277#define VNETHDR_GSO_NONE 0 // Not a GSO frame
278#define VNETHDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
279#define VNETHDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
280#define VNETHDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
281#define VNETHDR_GSO_ECN 0x80 // TCP has ECN set
282
283struct VNetHdr
284{
285 uint8_t u8Flags;
286 uint8_t u8GSOType;
287 uint16_t u16HdrLen;
288 uint16_t u16GSOSize;
289 uint16_t u16CSumStart;
290 uint16_t u16CSumOffset;
291};
292typedef struct VNetHdr VNETHDR;
293typedef VNETHDR *PVNETHDR;
294AssertCompileSize(VNETHDR, 10);
295
296struct VNetHdrMrx
297{
298 VNETHDR Hdr;
299 uint16_t u16NumBufs;
300};
301typedef struct VNetHdrMrx VNETHDRMRX;
302typedef VNETHDRMRX *PVNETHDRMRX;
303AssertCompileSize(VNETHDRMRX, 12);
304
305AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
306
307#define VNET_OK 0
308#define VNET_ERROR 1
309typedef uint8_t VNETCTLACK;
310
311#define VNET_CTRL_CLS_RX_MODE 0
312#define VNET_CTRL_CMD_RX_MODE_PROMISC 0
313#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
314
315#define VNET_CTRL_CLS_MAC 1
316#define VNET_CTRL_CMD_MAC_TABLE_SET 0
317
318#define VNET_CTRL_CLS_VLAN 2
319#define VNET_CTRL_CMD_VLAN_ADD 0
320#define VNET_CTRL_CMD_VLAN_DEL 1
321
322
323typedef struct VNetCtlHdr
324{
325 uint8_t u8Class;
326 uint8_t u8Command;
327} VNETCTLHDR;
328AssertCompileSize(VNETCTLHDR, 2);
329typedef VNETCTLHDR *PVNETCTLHDR;
330
331
332
333#ifdef IN_RING3
334
335/** Returns true if large packets are written into several RX buffers. */
336DECLINLINE(bool) vnetR3MergeableRxBuffers(PVNETSTATE pThis)
337{
338 return !!(pThis->VPCI.uGuestFeatures & VNET_F_MRG_RXBUF);
339}
340
341#define VNET_R3_CS_ENTER_RETURN_VOID(a_pDevIns, a_pThis) VPCI_R3_CS_ENTER_RETURN_VOID(a_pDevIns, &(a_pThis)->VPCI)
342
343DECLINLINE(int) vnetR3CsEnter(PPDMDEVINS pDevIns, PVNETSTATE pThis, int rcBusy)
344{
345 return vpciCsEnter(pDevIns, &pThis->VPCI, rcBusy);
346}
347
348DECLINLINE(void) vnetR3CsLeave(PPDMDEVINS pDevIns, PVNETSTATE pThis)
349{
350 vpciCsLeave(pDevIns, &pThis->VPCI);
351}
352
353
354DECLINLINE(int) vnetCsRxEnter(PVNETSTATE pThis, int rcBusy)
355{
356 RT_NOREF_PV(pThis);
357 RT_NOREF_PV(rcBusy);
358 // STAM_PROFILE_START(&pThis->CTXSUFF(StatCsRx), a);
359 // int rc = PDMCritSectEnter(&pThis->csRx, rcBusy);
360 // STAM_PROFILE_STOP(&pThis->CTXSUFF(StatCsRx), a);
361 // return rc;
362 return VINF_SUCCESS;
363}
364
365DECLINLINE(void) vnetCsRxLeave(PVNETSTATE pThis)
366{
367 RT_NOREF_PV(pThis);
368 // PDMCritSectLeave(&pThis->csRx);
369}
370
371/**
372 * A helper function to detect the link state to the other side of "the wire".
373 *
374 * When deciding to bring up the link we need to take into account both if the
375 * cable is connected and if our device is actually connected to the outside
376 * world. If no driver is attached we won't start the TX thread nor we will
377 * initialize the TX semaphore, which is a problem for the TX queue handler.
378 *
379 * @returns true if the device is connected to something.
380 *
381 * @param pDevIns The device instance.
382 */
383DECLINLINE(bool) vnetR3IsConnected(PPDMDEVINS pDevIns)
384{
385 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
386 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
387 return pThis->fCableConnected && pThisCC->pDrv;
388}
389
390/**
391 * Dump a packet to debug log.
392 *
393 * @param pThis The virtio-net shared instance data.
394 * @param pbPacket The packet.
395 * @param cb The size of the packet.
396 * @param pszText A string denoting direction of packet transfer.
397 */
398DECLINLINE(void) vnetR3PacketDump(PVNETSTATE pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
399{
400# ifdef DEBUG
401# if 0
402 Log(("%s %s packet #%d (%d bytes):\n",
403 INSTANCE(pThis), pszText, ++pThis->u32PktNo, cb));
404 Log3(("%.*Rhxd\n", cb, pbPacket));
405# else
406 vboxEthPacketDump(INSTANCE(pThis), pszText, pbPacket, (uint32_t)cb);
407# endif
408# else
409 RT_NOREF4(pThis, pbPacket, cb, pszText);
410# endif
411}
412#endif /* IN_RING3 */
413
414/**
415 * Print features given in uFeatures to debug log.
416 *
417 * @param pThis The virtio-net shared instance data.
418 * @param fFeatures Descriptions of which features to print.
419 * @param pcszText A string to print before the list of features.
420 */
421DECLINLINE(void) vnetPrintFeatures(PVNETSTATE pThis, uint32_t fFeatures, const char *pcszText)
422{
423#ifdef LOG_ENABLED
424 static struct
425 {
426 uint32_t fMask;
427 const char *pcszDesc;
428 } const s_aFeatures[] =
429 {
430 { VNET_F_CSUM, "host handles pkts w/ partial csum" },
431 { VNET_F_GUEST_CSUM, "guest handles pkts w/ partial csum" },
432 { VNET_F_MAC, "host has given MAC address" },
433 { VNET_F_GSO, "host handles pkts w/ any GSO type" },
434 { VNET_F_GUEST_TSO4, "guest can handle TSOv4 in" },
435 { VNET_F_GUEST_TSO6, "guest can handle TSOv6 in" },
436 { VNET_F_GUEST_ECN, "guest can handle TSO[6] w/ ECN in" },
437 { VNET_F_GUEST_UFO, "guest can handle UFO in" },
438 { VNET_F_HOST_TSO4, "host can handle TSOv4 in" },
439 { VNET_F_HOST_TSO6, "host can handle TSOv6 in" },
440 { VNET_F_HOST_ECN, "host can handle TSO[6] w/ ECN in" },
441 { VNET_F_HOST_UFO, "host can handle UFO in" },
442 { VNET_F_MRG_RXBUF, "host can merge receive buffers" },
443 { VNET_F_STATUS, "virtio_net_config.status available" },
444 { VNET_F_CTRL_VQ, "control channel available" },
445 { VNET_F_CTRL_RX, "control channel RX mode support" },
446 { VNET_F_CTRL_VLAN, "control channel VLAN filtering" }
447 };
448
449 Log3(("%s %s:\n", INSTANCE(pThis), pcszText));
450 for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
451 {
452 if (s_aFeatures[i].fMask & fFeatures)
453 Log3(("%s --> %s\n", INSTANCE(pThis), s_aFeatures[i].pcszDesc));
454 }
455#else /* !LOG_ENABLED */
456 RT_NOREF3(pThis, fFeatures, pcszText);
457#endif /* !LOG_ENABLED */
458}
459
460/**
461 * @interface_method_impl{VPCIIOCALLBACKS,pfnGetHostFeatures}
462 */
463static DECLCALLBACK(uint32_t) vnetIoCb_GetHostFeatures(PVPCISTATE pVPciState)
464{
465 RT_NOREF_PV(pVPciState);
466
467 /* We support:
468 * - Host-provided MAC address
469 * - Link status reporting in config space
470 * - Control queue
471 * - RX mode setting
472 * - MAC filter table
473 * - VLAN filter
474 */
475 return VNET_F_MAC
476 | VNET_F_STATUS
477 | VNET_F_CTRL_VQ
478 | VNET_F_CTRL_RX
479 | VNET_F_CTRL_VLAN
480#ifdef VNET_WITH_GSO
481 | VNET_F_CSUM
482 | VNET_F_HOST_TSO4
483 | VNET_F_HOST_TSO6
484 | VNET_F_HOST_UFO
485 | VNET_F_GUEST_CSUM /* We expect the guest to accept partial TCP checksums (see @bugref{4796}) */
486 | VNET_F_GUEST_TSO4
487 | VNET_F_GUEST_TSO6
488 | VNET_F_GUEST_UFO
489#endif
490#ifdef VNET_WITH_MERGEABLE_RX_BUFS
491 | VNET_F_MRG_RXBUF
492#endif
493 ;
494}
495
496/**
497 * @interface_method_impl{VPCIIOCALLBACKS,pfnGetHostMinimalFeatures}
498 */
499static DECLCALLBACK(uint32_t) vnetIoCb_GetHostMinimalFeatures(PVPCISTATE pVPciState)
500{
501 RT_NOREF_PV(pVPciState);
502 return VNET_F_MAC;
503}
504
505/**
506 * @interface_method_impl{VPCIIOCALLBACKS,pfnSetHostFeatures}
507 */
508static DECLCALLBACK(void) vnetIoCb_SetHostFeatures(PVPCISTATE pVPciState, uint32_t fFeatures)
509{
510 PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
511 /** @todo Nothing to do here yet */
512 LogFlow(("%s vnetIoCb_SetHostFeatures: uFeatures=%x\n", INSTANCE(pThis), fFeatures));
513 vnetPrintFeatures(pThis, fFeatures, "The guest negotiated the following features");
514}
515
516/**
517 * @interface_method_impl{VPCIIOCALLBACKS,pfnGetConfig}
518 */
519static DECLCALLBACK(int) vnetIoCb_GetConfig(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *data)
520{
521 PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
522 if (offCfg + cb > sizeof(struct VNetPCIConfig))
523 {
524 Log(("%s vnetIoCb_GetConfig: Read beyond the config structure is attempted (offCfg=%#x cb=%x).\n", INSTANCE(pThis), offCfg, cb));
525 return VERR_IOM_IOPORT_UNUSED;
526 }
527 memcpy(data, (uint8_t *)&pThis->config + offCfg, cb);
528 return VINF_SUCCESS;
529}
530
531/**
532 * @interface_method_impl{VPCIIOCALLBACKS,pfnSetConfig}
533 */
534static DECLCALLBACK(int) vnetIoCb_SetConfig(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *data)
535{
536 PVNETSTATE pThis = RT_FROM_MEMBER(pVPciState, VNETSTATE, VPCI);
537 if (offCfg + cb > sizeof(struct VNetPCIConfig))
538 {
539 Log(("%s vnetIoCb_SetConfig: Write beyond the config structure is attempted (offCfg=%#x cb=%x).\n", INSTANCE(pThis), offCfg, cb));
540 if (offCfg < sizeof(struct VNetPCIConfig))
541 memcpy((uint8_t *)&pThis->config + offCfg, data, sizeof(struct VNetPCIConfig) - offCfg);
542 return VINF_SUCCESS;
543 }
544 memcpy((uint8_t *)&pThis->config + offCfg, data, cb);
545 return VINF_SUCCESS;
546}
547
548/**
549 * @interface_method_impl{VPCIIOCALLBACKS,pfnReset}
550 *
551 * Hardware reset. Revert all registers to initial values.
552 */
553static DECLCALLBACK(int) vnetIoCb_Reset(PPDMDEVINS pDevIns)
554{
555#ifndef IN_RING3
556 RT_NOREF(pDevIns);
557 return VINF_IOM_R3_IOPORT_WRITE;
558#else
559 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
560 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
561
562 Log(("%s Reset triggered\n", INSTANCE(pThis)));
563
564 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
565 if (RT_UNLIKELY(rc != VINF_SUCCESS))
566 {
567 LogRel(("vnetIoCb_Reset failed to enter RX critical section!\n"));
568 return rc;
569 }
570 vpciReset(pDevIns, &pThis->VPCI);
571 vnetCsRxLeave(pThis);
572
573 /// @todo Implement reset
574 if (vnetR3IsConnected(pDevIns))
575 pThis->config.uStatus = VNET_S_LINK_UP;
576 else
577 pThis->config.uStatus = 0;
578 Log(("%s vnetIoCb_Reset: Link is %s\n", INSTANCE(pThis), vnetR3IsConnected(pDevIns) ? "up" : "down"));
579
580 /*
581 * By default we pass all packets up since the older guests cannot control
582 * virtio mode.
583 */
584 pThis->fPromiscuous = true;
585 pThis->fAllMulti = false;
586 pThis->cMacFilterEntries = 0;
587 memset(pThis->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
588 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
589 pThis->uIsTransmitting = 0;
590 if (pThisCC->pDrv)
591 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
592 return VINF_SUCCESS;
593#endif
594}
595
596
597/**
598 * Wakeup the RX thread.
599 */
600static void vnetWakeupReceive(PPDMDEVINS pDevIns)
601{
602 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
603 if ( pThis->fMaybeOutOfSpace
604 && pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
605 {
606 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
607 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pThis)));
608 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
609 AssertRC(rc);
610 }
611}
612
613#ifdef IN_RING3
614
615/**
616 * Helper function that raises an interrupt if the guest is ready to receive it.
617 */
618static int vnetR3RaiseInterrupt(PPDMDEVINS pDevIns, PVNETSTATE pThis, int rcBusy, uint8_t u8IntCause)
619{
620 if (pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK)
621 return vpciRaiseInterrupt(pDevIns, &pThis->VPCI, rcBusy, u8IntCause);
622 return rcBusy;
623}
624
625
626/**
627 * Takes down the link temporarily if it's current status is up.
628 *
629 * This is used during restore and when replumbing the network link.
630 *
631 * The temporary link outage is supposed to indicate to the OS that all network
632 * connections have been lost and that it for instance is appropriate to
633 * renegotiate any DHCP lease.
634 *
635 * @param pDevIns The device instance.
636 * @param pThis The virtio-net shared instance data.
637 * @param pThisCC The virtio-net ring-3 instance data.
638 */
639static void vnetR3TempLinkDown(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
640{
641 if (pThis->config.uStatus & VNET_S_LINK_UP)
642 {
643 pThis->config.uStatus &= ~VNET_S_LINK_UP;
644 vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
645 /* Restore the link back in 5 seconds. */
646 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
647 AssertRC(rc);
648 Log(("%s vnetR3TempLinkDown: Link is down temporarily\n", INSTANCE(pThis)));
649 }
650}
651
652
653/**
654 * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
655 */
656static DECLCALLBACK(void) vnetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
657{
658 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
659 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
660 RT_NOREF(hTimer, pvUser);
661
662 VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
663
664 Log(("%s vnetR3LinkUpTimer: connected=%s\n", INSTANCE(pThis), vnetR3IsConnected(pDevIns)?"true":"false"));
665 /* Do not bring up the link if the device is not connected. */
666 if (vnetR3IsConnected(pDevIns))
667 {
668 pThis->config.uStatus |= VNET_S_LINK_UP;
669 vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
670 vnetWakeupReceive(pDevIns);
671 }
672
673 vnetR3CsLeave(pDevIns, pThis);
674
675 Log(("%s vnetR3LinkUpTimer: Link is up\n", INSTANCE(pThis)));
676 if (pThisCC->pDrv)
677 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
678}
679
680#endif /* IN_RING3 */
681
682/**
683 * @interface_method_impl{VPCIIOCALLBACKS,pfnReady}
684 *
685 * This function is called when the driver becomes ready.
686 */
687static DECLCALLBACK(void) vnetIoCb_Ready(PPDMDEVINS pDevIns)
688{
689 Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(PDMDEVINS_2_DATA(pDevIns, PVNETSTATE))));
690 vnetWakeupReceive(pDevIns);
691}
692
693
694/**
695 * I/O port callbacks.
696 */
697static const VPCIIOCALLBACKS g_IOCallbacks =
698{
699 vnetIoCb_GetHostFeatures,
700 vnetIoCb_GetHostMinimalFeatures,
701 vnetIoCb_SetHostFeatures,
702 vnetIoCb_GetConfig,
703 vnetIoCb_SetConfig,
704 vnetIoCb_Reset,
705 vnetIoCb_Ready,
706};
707
708
709/**
710 * @callback_method_impl{FNIOMIOPORTNEWIN}
711 */
712static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
713{
714 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
715 RT_NOREF(pvUser);
716 return vpciIOPortIn(pDevIns, &pThis->VPCI, offPort, pu32, cb, &g_IOCallbacks);
717}
718
719
720/**
721 * @callback_method_impl{FNIOMIOPORTNEWOUT}
722 */
723static DECLCALLBACK(VBOXSTRICTRC) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
724{
725 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
726 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
727 RT_NOREF(pvUser);
728 return vpciIOPortOut(pDevIns, &pThis->VPCI, &pThisCC->VPCI, offPort, u32, cb, &g_IOCallbacks);
729}
730
731
732#ifdef IN_RING3
733
734/**
735 * Check if the device can receive data now.
736 * This must be called before the pfnRecieve() method is called.
737 *
738 * @remarks As a side effect this function enables queue notification
739 * if it cannot receive because the queue is empty.
740 * It disables notification if it can receive.
741 *
742 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
743 * @thread RX
744 */
745static int vnetR3CanReceive(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
746{
747 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
748 AssertRCReturn(rc, rc);
749
750 LogFlow(("%s vnetR3CanReceive\n", INSTANCE(pThis)));
751 if (!(pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK))
752 rc = VERR_NET_NO_BUFFER_SPACE;
753 else if (!vqueueIsReady(pThisCC->pRxQueue))
754 rc = VERR_NET_NO_BUFFER_SPACE;
755 else if (vqueueIsEmpty(pDevIns, pThisCC->pRxQueue))
756 {
757 vringSetNotification(pDevIns, &pThisCC->pRxQueue->VRing, true);
758 rc = VERR_NET_NO_BUFFER_SPACE;
759 }
760 else
761 {
762 vringSetNotification(pDevIns, &pThisCC->pRxQueue->VRing, false);
763 rc = VINF_SUCCESS;
764 }
765
766 LogFlow(("%s vnetR3CanReceive -> %Rrc\n", INSTANCE(pThis), rc));
767 vnetCsRxLeave(pThis);
768 return rc;
769}
770
771/**
772 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
773 */
774static DECLCALLBACK(int) vnetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
775{
776 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
777 PPDMDEVINS pDevIns = pThisCC->pDevIns;
778 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
779 LogFlow(("%s vnetR3NetworkDown_WaitReceiveAvail(cMillies=%u)\n", INSTANCE(pThis), cMillies));
780
781 int rc = vnetR3CanReceive(pDevIns, pThis, pThisCC);
782 if (RT_SUCCESS(rc))
783 return VINF_SUCCESS;
784
785 if (cMillies == 0)
786 return VERR_NET_NO_BUFFER_SPACE;
787
788 rc = VERR_INTERRUPTED;
789 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
790 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
791
792 VMSTATE enmVMState;
793 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
794 || enmVMState == VMSTATE_RUNNING_LS))
795 {
796 int rc2 = vnetR3CanReceive(pDevIns, pThis, pThisCC);
797 if (RT_SUCCESS(rc2))
798 {
799 rc = VINF_SUCCESS;
800 break;
801 }
802 Log(("%s vnetR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", INSTANCE(pThis), cMillies));
803 rc2 = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventMoreRxDescAvail, cMillies);
804 if (RT_FAILURE(rc2) && rc2 != VERR_TIMEOUT && rc2 != VERR_INTERRUPTED)
805 RTThreadSleep(1);
806 }
807 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
808 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
809
810 LogFlow(("%s vnetR3NetworkDown_WaitReceiveAvail -> %d\n", INSTANCE(pThis), rc));
811 return rc;
812}
813
814
815/**
816 * @interface_method_impl{PDMIBASE,pfnQueryInterface,
817 * For VNETSTATECC::VPCI.IBase}
818 */
819static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
820{
821 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, VPCI.IBase);
822
823 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
824 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
825
826 return vpciR3QueryInterface(&pThisCC->VPCI, pszIID);
827}
828
829/**
830 * Returns true if it is a broadcast packet.
831 *
832 * @returns true if destination address indicates broadcast.
833 * @param pvBuf The ethernet packet.
834 */
835DECLINLINE(bool) vnetR3IsBroadcast(const void *pvBuf)
836{
837 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
838 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
839}
840
841/**
842 * Returns true if it is a multicast packet.
843 *
844 * @remarks returns true for broadcast packets as well.
845 * @returns true if destination address indicates multicast.
846 * @param pvBuf The ethernet packet.
847 */
848DECLINLINE(bool) vnetR3IsMulticast(const void *pvBuf)
849{
850 return (*(char*)pvBuf) & 1;
851}
852
853/**
854 * Determines if the packet is to be delivered to upper layer.
855 *
856 * @returns true if packet is intended for this node.
857 * @param pThis Pointer to the state structure.
858 * @param pvBuf The ethernet packet.
859 * @param cb Number of bytes available in the packet.
860 */
861static bool vnetR3AddressFilter(PVNETSTATE pThis, const void *pvBuf, size_t cb)
862{
863 if (pThis->fPromiscuous)
864 return true;
865
866 /* Ignore everything outside of our VLANs */
867 uint16_t *u16Ptr = (uint16_t*)pvBuf;
868 /* Compare TPID with VLAN Ether Type */
869 if ( u16Ptr[6] == RT_H2BE_U16(0x8100)
870 && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
871 {
872 Log4(("%s vnetR3AddressFilter: not our VLAN, returning false\n", INSTANCE(pThis)));
873 return false;
874 }
875
876 if (vnetR3IsBroadcast(pvBuf))
877 return true;
878
879 if (pThis->fAllMulti && vnetR3IsMulticast(pvBuf))
880 return true;
881
882 if (!memcmp(pThis->config.mac.au8, pvBuf, sizeof(RTMAC)))
883 return true;
884 Log4(("%s vnetR3AddressFilter: %RTmac (conf) != %RTmac (dest)\n", INSTANCE(pThis), pThis->config.mac.au8, pvBuf));
885
886 for (unsigned i = 0; i < pThis->cMacFilterEntries; i++)
887 if (!memcmp(&pThis->aMacFilter[i], pvBuf, sizeof(RTMAC)))
888 return true;
889
890 Log2(("%s vnetR3AddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pThis)));
891 vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
892
893 return false;
894}
895
896/**
897 * Pad and store received packet.
898 *
899 * @remarks Make sure that the packet appears to upper layer as one coming
900 * from real Ethernet: pad it and insert FCS.
901 *
902 * @returns VBox status code.
903 * @param pDevIns The device instance.
904 * @param pThis The virtio-net shared instance data.
905 * @param pvBuf The available data.
906 * @param cb Number of bytes available in the buffer.
907 * @thread RX
908 */
909static int vnetR3HandleRxPacket(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
910 const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
911{
912 VNETHDRMRX Hdr;
913 unsigned cbHdr;
914 RTGCPHYS addrHdrMrx = 0;
915
916 if (pGso)
917 {
918 Log2(("%s vnetR3HandleRxPacket: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
919 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
920 Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
921 switch (pGso->u8Type)
922 {
923 case PDMNETWORKGSOTYPE_IPV4_TCP:
924 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
925 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
926 break;
927 case PDMNETWORKGSOTYPE_IPV6_TCP:
928 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
929 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
930 break;
931 case PDMNETWORKGSOTYPE_IPV4_UDP:
932 Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
933 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
934 break;
935 default:
936 return VERR_INVALID_PARAMETER;
937 }
938 Hdr.Hdr.u16HdrLen = pGso->cbHdrsTotal;
939 Hdr.Hdr.u16GSOSize = pGso->cbMaxSeg;
940 Hdr.Hdr.u16CSumStart = pGso->offHdr2;
941 Hdr.u16NumBufs = 0;
942 STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
943 }
944 else
945 {
946 Hdr.Hdr.u8Flags = 0;
947 Hdr.Hdr.u8GSOType = VNETHDR_GSO_NONE;
948 Hdr.Hdr.u16HdrLen = 0;
949 Hdr.Hdr.u16GSOSize = 0;
950 Hdr.Hdr.u16CSumStart = 0;
951 Hdr.Hdr.u16CSumOffset = 0;
952 Hdr.u16NumBufs = 0;
953 }
954
955 if (vnetR3MergeableRxBuffers(pThis))
956 cbHdr = sizeof(VNETHDRMRX);
957 else
958 cbHdr = sizeof(VNETHDR);
959
960 vnetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
961
962 unsigned int uOffset = 0;
963 unsigned int nElem;
964 for (nElem = 0; uOffset < cb; nElem++)
965 {
966 VQUEUEELEM elem;
967 unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
968
969 if (!vqueueGet(pDevIns, &pThis->VPCI, pThisCC->pRxQueue, &elem))
970 {
971 /*
972 * @todo: It is possible to run out of RX buffers if only a few
973 * were added and we received a big packet.
974 */
975 Log(("%s vnetR3HandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pThis)));
976 return VERR_INTERNAL_ERROR;
977 }
978
979 if (elem.cIn < 1)
980 {
981 Log(("%s vnetR3HandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pThis)));
982 return VERR_INTERNAL_ERROR;
983 }
984
985 if (nElem == 0)
986 {
987 if (vnetR3MergeableRxBuffers(pThis))
988 {
989 addrHdrMrx = elem.aSegsIn[nSeg].addr;
990 cbReserved = cbHdr;
991 }
992 else
993 {
994 /* The very first segment of the very first element gets the header. */
995 if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
996 {
997 Log(("%s vnetR3HandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pThis)));
998 return VERR_INTERNAL_ERROR;
999 }
1000 elem.aSegsIn[nSeg++].pv = &Hdr;
1001 }
1002 uElemSize += cbHdr;
1003 }
1004 while (nSeg < elem.cIn && uOffset < cb)
1005 {
1006 unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
1007 cb - uOffset);
1008 elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
1009 uOffset += uSize;
1010 uElemSize += uSize;
1011 }
1012 STAM_PROFILE_START(&pThis->StatReceiveStore, a);
1013 vqueuePut(pDevIns, &pThis->VPCI, pThisCC->pRxQueue, &elem, uElemSize, cbReserved);
1014 STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
1015 if (!vnetR3MergeableRxBuffers(pThis))
1016 break;
1017 cbReserved = 0;
1018 }
1019 if (vnetR3MergeableRxBuffers(pThis))
1020 {
1021 Hdr.u16NumBufs = nElem;
1022 int rc = PDMDevHlpPCIPhysWrite(pDevIns, addrHdrMrx,
1023 &Hdr, sizeof(Hdr));
1024 if (RT_FAILURE(rc))
1025 {
1026 Log(("%s vnetR3HandleRxPacket: Failed to write merged RX buf header: %Rrc\n", INSTANCE(pThis), rc));
1027 return rc;
1028 }
1029 }
1030 vqueueSync(pDevIns, &pThis->VPCI, pThisCC->pRxQueue);
1031 if (uOffset < cb)
1032 {
1033 Log(("%s vnetR3HandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n", INSTANCE(pThis), cb));
1034 return VERR_TOO_MUCH_DATA;
1035 }
1036
1037 return VINF_SUCCESS;
1038}
1039
1040/**
1041 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
1042 */
1043static DECLCALLBACK(int) vnetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf,
1044 size_t cb, PCPDMNETWORKGSO pGso)
1045{
1046 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
1047 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1048 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1049
1050 if (pGso)
1051 {
1052 uint32_t uFeatures = pThis->VPCI.uGuestFeatures;
1053
1054 switch (pGso->u8Type)
1055 {
1056 case PDMNETWORKGSOTYPE_IPV4_TCP:
1057 uFeatures &= VNET_F_GUEST_TSO4;
1058 break;
1059 case PDMNETWORKGSOTYPE_IPV6_TCP:
1060 uFeatures &= VNET_F_GUEST_TSO6;
1061 break;
1062 case PDMNETWORKGSOTYPE_IPV4_UDP:
1063 case PDMNETWORKGSOTYPE_IPV6_UDP:
1064 uFeatures &= VNET_F_GUEST_UFO;
1065 break;
1066 default:
1067 uFeatures = 0;
1068 break;
1069 }
1070 if (!uFeatures)
1071 {
1072 Log2(("%s vnetR3NetworkDown_ReceiveGso: GSO type (0x%x) not supported\n", INSTANCE(pThis), pGso->u8Type));
1073 return VERR_NOT_SUPPORTED;
1074 }
1075 }
1076
1077 Log2(("%s vnetR3NetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n", INSTANCE(pThis), pvBuf, cb, pGso));
1078 int rc = vnetR3CanReceive(pDevIns, pThis, pThisCC);
1079 if (RT_FAILURE(rc))
1080 return rc;
1081
1082 /* Drop packets if VM is not running or cable is disconnected. */
1083 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
1084 if (( enmVMState != VMSTATE_RUNNING
1085 && enmVMState != VMSTATE_RUNNING_LS)
1086 || !(pThis->config.uStatus & VNET_S_LINK_UP))
1087 return VINF_SUCCESS;
1088
1089 STAM_PROFILE_START(&pThis->StatReceive, a);
1090 vpciR3SetReadLed(&pThis->VPCI, true);
1091 if (vnetR3AddressFilter(pThis, pvBuf, cb))
1092 {
1093 rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
1094 if (RT_SUCCESS(rc))
1095 {
1096 rc = vnetR3HandleRxPacket(pDevIns, pThis, pThisCC, pvBuf, cb, pGso);
1097 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
1098 vnetCsRxLeave(pThis);
1099 }
1100 }
1101 vpciR3SetReadLed(&pThis->VPCI, false);
1102 STAM_PROFILE_STOP(&pThis->StatReceive, a);
1103 return rc;
1104}
1105
1106/**
1107 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
1108 */
1109static DECLCALLBACK(int) vnetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
1110{
1111 return vnetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
1112}
1113
1114/**
1115 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
1116 */
1117static DECLCALLBACK(int) vnetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1118{
1119 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
1120 PVNETSTATE pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
1121 memcpy(pMac, pThis->config.mac.au8, sizeof(RTMAC));
1122 return VINF_SUCCESS;
1123}
1124
1125/**
1126 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
1127 */
1128static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
1129{
1130 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
1131 PVNETSTATE pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
1132 if (pThis->config.uStatus & VNET_S_LINK_UP)
1133 return PDMNETWORKLINKSTATE_UP;
1134 return PDMNETWORKLINKSTATE_DOWN;
1135}
1136
1137/**
1138 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
1139 */
1140static DECLCALLBACK(int) vnetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
1141{
1142 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkConfig);
1143 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1144 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1145 bool fOldUp = !!(pThis->config.uStatus & VNET_S_LINK_UP);
1146 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
1147
1148 Log(("%s vnetR3NetworkConfig_SetLinkState: enmState=%d\n", INSTANCE(pThis), enmState));
1149 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
1150 {
1151 if (fOldUp)
1152 {
1153 /*
1154 * We bother to bring the link down only if it was up previously. The UP link state
1155 * notification will be sent when the link actually goes up in vnetR3LinkUpTimer().
1156 */
1157 vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
1158 if (pThisCC->pDrv)
1159 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
1160 }
1161 }
1162 else if (fNewUp != fOldUp)
1163 {
1164 if (fNewUp)
1165 {
1166 pThis->fCableConnected = true;
1167 /* The link state depends both on the cable connected and device attached. */
1168 if (vnetR3IsConnected(pDevIns))
1169 {
1170 Log(("%s Link is up\n", INSTANCE(pThis)));
1171 pThis->config.uStatus |= VNET_S_LINK_UP;
1172 vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1173 }
1174 }
1175 else
1176 {
1177 /* The link was brought down explicitly, make sure it won't come up by timer. */
1178 PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
1179 Log(("%s Link is down\n", INSTANCE(pThis)));
1180 pThis->fCableConnected = false;
1181 pThis->config.uStatus &= ~VNET_S_LINK_UP;
1182 vnetR3RaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1183 }
1184 if (pThisCC->pDrv)
1185 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
1186 }
1187 return VINF_SUCCESS;
1188}
1189
1190/**
1191 * @callback_method_impl{FNVPCIQUEUECALLBACK, The RX queue}
1192 */
1193static DECLCALLBACK(void) vnetR3QueueReceive(PPDMDEVINS pDevIns, PVQUEUE pQueue)
1194{
1195 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1196 RT_NOREF(pThis, pQueue);
1197 Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pThis)));
1198 vnetWakeupReceive(pDevIns);
1199}
1200
1201/**
1202 * Sets up the GSO context according to the Virtio header.
1203 *
1204 * @param pGso The GSO context to setup.
1205 * @param pCtx The context descriptor.
1206 */
1207DECLINLINE(PPDMNETWORKGSO) vnetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
1208{
1209 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
1210
1211 if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
1212 {
1213 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
1214 return NULL;
1215 }
1216 switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
1217 {
1218 case VNETHDR_GSO_TCPV4:
1219 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
1220 pGso->cbHdrsSeg = pHdr->u16HdrLen;
1221 break;
1222 case VNETHDR_GSO_TCPV6:
1223 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
1224 pGso->cbHdrsSeg = pHdr->u16HdrLen;
1225 break;
1226 case VNETHDR_GSO_UDP:
1227 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
1228 pGso->cbHdrsSeg = pHdr->u16CSumStart;
1229 break;
1230 default:
1231 return NULL;
1232 }
1233 if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
1234 pGso->offHdr2 = pHdr->u16CSumStart;
1235 else
1236 {
1237 AssertMsgFailed(("GSO without checksum offloading!\n"));
1238 return NULL;
1239 }
1240 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1241 pGso->cbHdrsTotal = pHdr->u16HdrLen;
1242 pGso->cbMaxSeg = pHdr->u16GSOSize;
1243 return pGso;
1244}
1245
1246DECLINLINE(uint16_t) vnetR3CSum16(const void *pvBuf, size_t cb)
1247{
1248 uint32_t csum = 0;
1249 uint16_t *pu16 = (uint16_t *)pvBuf;
1250
1251 while (cb > 1)
1252 {
1253 csum += *pu16++;
1254 cb -= 2;
1255 }
1256 if (cb)
1257 csum += *(uint8_t*)pu16;
1258 while (csum >> 16)
1259 csum = (csum >> 16) + (csum & 0xFFFF);
1260 return ~csum;
1261}
1262
1263DECLINLINE(void) vnetR3CompleteChecksum(uint8_t *pBuf, size_t cbSize, uint16_t uStart, uint16_t uOffset)
1264{
1265 AssertReturnVoid(uStart < cbSize);
1266 AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cbSize);
1267 *(uint16_t *)(pBuf + uStart + uOffset) = vnetR3CSum16(pBuf + uStart, cbSize - uStart);
1268}
1269
1270static bool vnetR3ReadHeader(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVNETHDR pHdr, uint32_t cbMax)
1271{
1272 int rc = PDMDevHlpPhysRead(pDevIns, GCPhys, pHdr, sizeof(*pHdr)); /** @todo r=bird: Why not PDMDevHlpPCIPhysRead? */
1273 if (RT_FAILURE(rc))
1274 return false;
1275
1276 Log4(("virtio-net: header flags=%x gso-type=%x len=%x gso-size=%x csum-start=%x csum-offset=%x cb=%x\n",
1277 pHdr->u8Flags, pHdr->u8GSOType, pHdr->u16HdrLen, pHdr->u16GSOSize, pHdr->u16CSumStart, pHdr->u16CSumOffset, cbMax));
1278
1279 if (pHdr->u8GSOType)
1280 {
1281 uint32_t u32MinHdrSize;
1282
1283 /* Segmentation offloading cannot be done without checksumming. */
1284 if (RT_UNLIKELY(!(pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)))
1285 return false;
1286 /* We do not support ECN. */
1287 if (RT_UNLIKELY(pHdr->u8GSOType & VNETHDR_GSO_ECN))
1288 return false;
1289 switch (pHdr->u8GSOType)
1290 {
1291 case VNETHDR_GSO_TCPV4:
1292 case VNETHDR_GSO_TCPV6:
1293 u32MinHdrSize = sizeof(RTNETTCP);
1294 break;
1295 case VNETHDR_GSO_UDP:
1296 u32MinHdrSize = 0;
1297 break;
1298 default:
1299 return false;
1300 }
1301 /* Header+MSS must not exceed the packet size. */
1302 if (RT_UNLIKELY(u32MinHdrSize + pHdr->u16CSumStart + pHdr->u16GSOSize > cbMax))
1303 return false;
1304 }
1305 /* Checksum must fit into the frame (validating both checksum fields). */
1306 if ( (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
1307 && sizeof(uint16_t) + pHdr->u16CSumStart + pHdr->u16CSumOffset > cbMax)
1308 return false;
1309 Log4(("virtio-net: return true\n"));
1310 return true;
1311}
1312
1313static int vnetR3TransmitFrame(PVNETSTATE pThis, PVNETSTATECC pThisCC, PPDMSCATTERGATHER pSgBuf,
1314 PPDMNETWORKGSO pGso, PVNETHDR pHdr)
1315{
1316 vnetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
1317 if (pGso)
1318 {
1319 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
1320 /*
1321 * We cannot use cdHdrs provided by the guest because of different ways
1322 * it gets filled out by different versions of kernels.
1323 */
1324 //if (pGso->cbHdrs < pHdr->u16CSumStart + pHdr->u16CSumOffset + 2)
1325 {
1326 Log4(("%s vnetR3TransmitPendingPackets: HdrLen before adjustment %d.\n",
1327 INSTANCE(pThis), pGso->cbHdrsTotal));
1328 switch (pGso->u8Type)
1329 {
1330 case PDMNETWORKGSOTYPE_IPV4_TCP:
1331 case PDMNETWORKGSOTYPE_IPV6_TCP:
1332 pGso->cbHdrsTotal = pHdr->u16CSumStart +
1333 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pHdr->u16CSumStart))->th_off * 4;
1334 AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
1335 ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
1336 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
1337 break;
1338 case PDMNETWORKGSOTYPE_IPV4_UDP:
1339 pGso->cbHdrsTotal = (uint8_t)(pHdr->u16CSumStart + sizeof(RTNETUDP));
1340 pGso->cbHdrsSeg = pHdr->u16CSumStart;
1341 break;
1342 }
1343 /* Update GSO structure embedded into the frame */
1344 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
1345 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
1346 Log4(("%s vnetR3TransmitPendingPackets: adjusted HdrLen to %d.\n",
1347 INSTANCE(pThis), pGso->cbHdrsTotal));
1348 }
1349 Log2(("%s vnetR3TransmitPendingPackets: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
1350 INSTANCE(pThis), pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1351 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
1352 }
1353 else if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
1354 {
1355 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
1356 /*
1357 * This is not GSO frame but checksum offloading is requested.
1358 */
1359 vnetR3CompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
1360 pHdr->u16CSumStart, pHdr->u16CSumOffset);
1361 }
1362
1363 return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, false);
1364}
1365
1366static void vnetR3TransmitPendingPackets(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
1367 PVQUEUE pQueue, bool fOnWorkerThread)
1368{
1369 /*
1370 * Let's deal with the cases we are not going to transmit anything,
1371 * to avoid setting 'uIsTransmitting' on and off.
1372 */
1373 if ((pThis->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
1374 {
1375 Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n", INSTANCE(pThis), pThis->VPCI.uStatus));
1376 return;
1377 }
1378
1379 if (!vnetR3IsConnected(pDevIns))
1380 {
1381 Log(("%s Ignoring transmit requests while cable is disconnected.\n", INSTANCE(pThis)));
1382 return;
1383 }
1384
1385 /*
1386 * Only one thread is allowed to transmit at a time, others should skip
1387 * transmission as the packets will be picked up by the transmitting
1388 * thread.
1389 */
1390 if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
1391 return;
1392
1393 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1394 if (pDrv)
1395 {
1396 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
1397 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
1398 if (rc == VERR_TRY_AGAIN)
1399 {
1400 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
1401 return;
1402 }
1403 }
1404
1405 unsigned int cbHdr;
1406 if (vnetR3MergeableRxBuffers(pThis))
1407 cbHdr = sizeof(VNETHDRMRX);
1408 else
1409 cbHdr = sizeof(VNETHDR);
1410
1411 Log3(("%s vnetR3TransmitPendingPackets: About to transmit %d pending packets\n",
1412 INSTANCE(pThis), vringReadAvailIndex(pDevIns, &pThisCC->pTxQueue->VRing) - pThisCC->pTxQueue->uNextAvailIndex));
1413
1414 vpciR3SetWriteLed(&pThis->VPCI, true);
1415
1416 /*
1417 * Do not remove descriptors from available ring yet, try to allocate the
1418 * buffer first.
1419 */
1420 VQUEUEELEM elem; /* This bugger is big! ~48KB on 64-bit hosts. */
1421 while (vqueuePeek(pDevIns, &pThis->VPCI, pQueue, &elem))
1422 {
1423 unsigned int uOffset = 0;
1424 if (elem.cOut < 2 || elem.aSegsOut[0].cb != cbHdr)
1425 {
1426 Log(("%s vnetR3QueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
1427 INSTANCE(pThis), elem.cOut, elem.aSegsOut[0].cb, cbHdr));
1428 break; /* For now we simply ignore the header, but it must be there anyway! */
1429 }
1430 RT_UNTRUSTED_VALIDATED_FENCE();
1431
1432 VNETHDR Hdr;
1433 unsigned int uSize = 0;
1434 STAM_PROFILE_ADV_START(&pThis->StatTransmit, a);
1435
1436 /* Compute total frame size. */
1437 for (unsigned int i = 1; i < elem.cOut && uSize < VNET_MAX_FRAME_SIZE; i++)
1438 uSize += elem.aSegsOut[i].cb;
1439 Log5(("%s vnetR3TransmitPendingPackets: complete frame is %u bytes.\n", INSTANCE(pThis), uSize));
1440 Assert(uSize <= VNET_MAX_FRAME_SIZE);
1441
1442 /* Truncate oversized frames. */
1443 if (uSize > VNET_MAX_FRAME_SIZE)
1444 uSize = VNET_MAX_FRAME_SIZE;
1445 if (pThisCC->pDrv && vnetR3ReadHeader(pDevIns, elem.aSegsOut[0].addr, &Hdr, uSize))
1446 {
1447 RT_UNTRUSTED_VALIDATED_FENCE();
1448 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
1449 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
1450
1451 PDMNETWORKGSO Gso;
1452 PDMNETWORKGSO *pGso = vnetR3SetupGsoCtx(&Gso, &Hdr);
1453
1454 /** @todo Optimize away the extra copying! (lazy bird) */
1455 PPDMSCATTERGATHER pSgBuf;
1456 int rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uSize, pGso, &pSgBuf);
1457 if (RT_SUCCESS(rc))
1458 {
1459 Assert(pSgBuf->cSegs == 1);
1460 pSgBuf->cbUsed = uSize;
1461
1462 /* Assemble a complete frame. */
1463 for (unsigned int i = 1; i < elem.cOut && uSize > 0; i++)
1464 {
1465 unsigned int cbSegment = RT_MIN(uSize, elem.aSegsOut[i].cb);
1466 PDMDevHlpPhysRead(pDevIns, elem.aSegsOut[i].addr,
1467 ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
1468 cbSegment);
1469 uOffset += cbSegment;
1470 uSize -= cbSegment;
1471 }
1472 rc = vnetR3TransmitFrame(pThis, pThisCC, pSgBuf, pGso, &Hdr);
1473 }
1474 else
1475 {
1476 Log4(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
1477 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
1478 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
1479 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
1480 break;
1481 }
1482
1483 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
1484 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
1485 }
1486
1487 /* Remove this descriptor chain from the available ring */
1488 vqueueSkip(pDevIns, &pThis->VPCI, pQueue);
1489 vqueuePut(pDevIns, &pThis->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1490 vqueueSync(pDevIns, &pThis->VPCI, pQueue);
1491 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
1492 }
1493 vpciR3SetWriteLed(&pThis->VPCI, false);
1494
1495 if (pDrv)
1496 pDrv->pfnEndXmit(pDrv);
1497 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
1498}
1499
1500/**
1501 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
1502 */
1503static DECLCALLBACK(void) vnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
1504{
1505 PVNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, VNETSTATECC, INetworkDown);
1506 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1507 PVNETSTATE pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVNETSTATE);
1508 STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
1509 vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/);
1510}
1511
1512# ifdef VNET_TX_DELAY
1513
1514/**
1515 * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
1516 */
1517static DECLCALLBACK(void) vnetR3QueueTransmit(PPDMDEVINS pDevIns, PVQUEUE pQueue)
1518{
1519 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1520 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
1521
1522 if (PDMDevHlpTimerIsActive(pDevIns, pThis->hTxTimer))
1523 {
1524 PDMDevHlpTimerStop(pDevIns, pThis->hTxTimer);
1525 Log3(("%s vnetR3QueueTransmit: Got kicked with notification disabled, re-enable notification and flush TX queue\n", INSTANCE(pThis)));
1526 vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pQueue, false /*fOnWorkerThread*/);
1527
1528 VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
1529
1530 vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
1531
1532 vnetR3CsLeave(pDevIns, pThis);
1533 }
1534 else
1535 {
1536 VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
1537
1538 vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, false);
1539 PDMDevHlpTimerSetMicro(pDevIns, pThis->hTxTimer, VNET_TX_DELAY);
1540 pThis->u64NanoTS = RTTimeNanoTS();
1541
1542 vnetR3CsLeave(pDevIns, pThis);
1543 }
1544}
1545
1546/**
1547 * @callback_method_impl{FNTMTIMERDEV, Transmit Delay Timer handler.}
1548 */
1549static DECLCALLBACK(void) vnetR3TxTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1550{
1551 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1552 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
1553 RT_NOREF(hTimer, pvUser);
1554
1555 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pThis->u64NanoTS) / 1000);
1556 if (u32MicroDiff < pThis->u32MinDiff)
1557 pThis->u32MinDiff = u32MicroDiff;
1558 if (u32MicroDiff > pThis->u32MaxDiff)
1559 pThis->u32MaxDiff = u32MicroDiff;
1560 pThis->u32AvgDiff = (pThis->u32AvgDiff * pThis->u32i + u32MicroDiff) / (pThis->u32i + 1);
1561 pThis->u32i++;
1562 Log3(("vnetR3TxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
1563 u32MicroDiff, pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
1564
1565// Log3(("%s vnetR3TxTimer: Expired\n", INSTANCE(pThis)));
1566 vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/);
1567
1568 VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
1569 vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
1570 vnetR3CsLeave(pDevIns, pThis);
1571}
1572
1573DECLINLINE(int) vnetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
1574{
1575 RT_NOREF(pDevIns, pThis, pThisCC);
1576 return VINF_SUCCESS;
1577}
1578
1579DECLINLINE(void) vnetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
1580{
1581 RT_NOREF(pDevIns, pThis, pThisCC);
1582}
1583
1584# else /* !VNET_TX_DELAY */
1585
1586/**
1587 * @callback_method_impl{FNPDMTHREADDEV}
1588 */
1589static DECLCALLBACK(int) vnetR3TxThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1590{
1591 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1592 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
1593
1594 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
1595 return VINF_SUCCESS;
1596
1597 int rc = VINF_SUCCESS;
1598 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1599 {
1600 rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTxEvent, RT_INDEFINITE_WAIT);
1601 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
1602 break;
1603 STAM_COUNTER_INC(&pThis->StatTransmitByThread);
1604 while (true)
1605 {
1606 vnetR3TransmitPendingPackets(pDevIns, pThis, pThisCC, pThisCC->pTxQueue, false /*fOnWorkerThread*/); /// @todo shouldn't it be true instead?
1607 Log(("vnetR3TxThread: enable kicking and get to sleep\n"));
1608 vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, true);
1609 /*
1610 * Break out of the loop if the device is not connected. Otherwise we will
1611 * end up in a tight loop, not being able to transmit, if there is something
1612 * in TX queue. See @bugref{10096}.
1613 */
1614 if (vqueueIsEmpty(pDevIns, pThisCC->pTxQueue) || !vnetR3IsConnected(pDevIns))
1615 break;
1616 vringSetNotification(pDevIns, &pThisCC->pTxQueue->VRing, false);
1617 }
1618 }
1619
1620 return rc;
1621}
1622
1623/**
1624 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
1625 */
1626static DECLCALLBACK(int) vnetR3TxThreadWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
1627{
1628 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1629 RT_NOREF(pThread);
1630 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
1631}
1632
1633static int vnetR3CreateTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
1634{
1635 int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTxEvent);
1636 if (RT_SUCCESS(rc))
1637 {
1638 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTxThread, NULL, vnetR3TxThread,
1639 vnetR3TxThreadWakeUp, 0, RTTHREADTYPE_IO, INSTANCE(pThis));
1640 if (RT_FAILURE(rc))
1641 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create worker thread %s"), INSTANCE(pThis));
1642 }
1643 else
1644 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("VNET: Failed to create SUP event semaphore"));
1645 return rc;
1646}
1647
1648static void vnetR3DestroyTxThreadAndEvent(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC)
1649{
1650 if (pThisCC->pTxThread)
1651 {
1652 /* Destroy the thread. */
1653 int rcThread;
1654 int rc = PDMDevHlpThreadDestroy(pDevIns, pThisCC->pTxThread, &rcThread);
1655 if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
1656 AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
1657 pThisCC->pTxThread = NULL;
1658 }
1659
1660 if (pThis->hTxEvent != NIL_SUPSEMEVENT)
1661 {
1662 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hTxEvent);
1663 pThis->hTxEvent = NIL_SUPSEMEVENT;
1664 }
1665}
1666
1667/**
1668 * @callback_method_impl{FNVPCIQUEUECALLBACK, The TX queue}
1669 */
1670static DECLCALLBACK(void) vnetR3QueueTransmit(PPDMDEVINS pDevIns, PVQUEUE pQueue)
1671{
1672 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1673
1674 Log(("vnetR3QueueTransmit: disable kicking and wake up TX thread\n"));
1675 vringSetNotification(pDevIns, &pQueue->VRing, false);
1676 int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTxEvent);
1677 AssertRC(rc);
1678}
1679
1680# endif /* !VNET_TX_DELAY */
1681
1682static uint8_t vnetR3ControlRx(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETSTATECC pThisCC,
1683 PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1684{
1685 uint8_t u8Ack = VNET_OK;
1686 uint8_t fOn, fDrvWasPromisc = pThis->fPromiscuous | pThis->fAllMulti;
1687 PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &fOn, sizeof(fOn));
1688 Log(("%s vnetR3ControlRx: uCommand=%u fOn=%u\n", INSTANCE(pThis), pCtlHdr->u8Command, fOn));
1689 switch (pCtlHdr->u8Command)
1690 {
1691 case VNET_CTRL_CMD_RX_MODE_PROMISC:
1692 pThis->fPromiscuous = !!fOn;
1693 break;
1694 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
1695 pThis->fAllMulti = !!fOn;
1696 break;
1697 default:
1698 u8Ack = VNET_ERROR;
1699 }
1700 if (fDrvWasPromisc != (pThis->fPromiscuous | pThis->fAllMulti) && pThisCC->pDrv)
1701 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv,
1702 (pThis->fPromiscuous | pThis->fAllMulti));
1703
1704 return u8Ack;
1705}
1706
1707static uint8_t vnetR3ControlMac(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1708{
1709 uint32_t nMacs = 0;
1710
1711 if ( pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1712 || pElem->cOut != 3
1713 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1714 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1715 {
1716 Log(("%s vnetR3ControlMac: Segment layout is wrong (u8Command=%u nOut=%u cb1=%u cb2=%u)\n",
1717 INSTANCE(pThis), pCtlHdr->u8Command, pElem->cOut, pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1718 return VNET_ERROR;
1719 }
1720
1721 /* Load unicast addresses */
1722 PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &nMacs, sizeof(nMacs));
1723
1724 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1725 {
1726 Log(("%s vnetR3ControlMac: The unicast mac segment is too small (nMacs=%u cb=%u)\n",
1727 INSTANCE(pThis), nMacs, pElem->aSegsOut[1].cb));
1728 return VNET_ERROR;
1729 }
1730
1731 if (nMacs > VNET_MAC_FILTER_LEN)
1732 {
1733 Log(("%s vnetR3ControlMac: MAC table is too big, have to use promiscuous mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
1734 pThis->fPromiscuous = true;
1735 }
1736 else
1737 {
1738 if (nMacs)
1739 PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr + sizeof(nMacs), pThis->aMacFilter, nMacs * sizeof(RTMAC));
1740 pThis->cMacFilterEntries = nMacs;
1741#ifdef LOG_ENABLED
1742 Log(("%s vnetR3ControlMac: unicast macs:\n", INSTANCE(pThis)));
1743 for(unsigned i = 0; i < nMacs; i++)
1744 Log((" %RTmac\n", &pThis->aMacFilter[i]));
1745#endif
1746 }
1747
1748 /* Load multicast addresses */
1749 PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[2].addr, &nMacs, sizeof(nMacs));
1750
1751 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1752 {
1753 Log(("%s vnetR3ControlMac: The multicast mac segment is too small (nMacs=%u cb=%u)\n",
1754 INSTANCE(pThis), nMacs, pElem->aSegsOut[2].cb));
1755 return VNET_ERROR;
1756 }
1757
1758 if (nMacs > VNET_MAC_FILTER_LEN - pThis->cMacFilterEntries)
1759 {
1760 Log(("%s vnetR3ControlMac: MAC table is too big, have to use allmulti mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
1761 pThis->fAllMulti = true;
1762 }
1763 else
1764 {
1765 if (nMacs)
1766 PDMDevHlpPhysRead(pDevIns,
1767 pElem->aSegsOut[2].addr + sizeof(nMacs),
1768 &pThis->aMacFilter[pThis->cMacFilterEntries],
1769 nMacs * sizeof(RTMAC));
1770#ifdef LOG_ENABLED
1771 Log(("%s vnetR3ControlMac: multicast macs:\n", INSTANCE(pThis)));
1772 for(unsigned i = 0; i < nMacs; i++)
1773 Log((" %RTmac\n",
1774 &pThis->aMacFilter[i+pThis->cMacFilterEntries]));
1775#endif
1776 pThis->cMacFilterEntries += nMacs;
1777 }
1778
1779 return VNET_OK;
1780}
1781
1782static uint8_t vnetR3ControlVlan(PPDMDEVINS pDevIns, PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1783{
1784 uint8_t u8Ack = VNET_OK;
1785 uint16_t u16Vid;
1786
1787 if (pElem->cOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1788 {
1789 Log(("%s vnetR3ControlVlan: Segment layout is wrong (u8Command=%u nOut=%u cb=%u)\n",
1790 INSTANCE(pThis), pCtlHdr->u8Command, pElem->cOut, pElem->aSegsOut[1].cb));
1791 return VNET_ERROR;
1792 }
1793
1794 PDMDevHlpPhysRead(pDevIns, pElem->aSegsOut[1].addr, &u16Vid, sizeof(u16Vid));
1795
1796 if (u16Vid >= VNET_MAX_VID)
1797 {
1798 Log(("%s vnetR3ControlVlan: VLAN ID is out of range (VID=%u)\n", INSTANCE(pThis), u16Vid));
1799 return VNET_ERROR;
1800 }
1801
1802 Log(("%s vnetR3ControlVlan: uCommand=%u VID=%u\n", INSTANCE(pThis), pCtlHdr->u8Command, u16Vid));
1803
1804 switch (pCtlHdr->u8Command)
1805 {
1806 case VNET_CTRL_CMD_VLAN_ADD:
1807 ASMBitSet(pThis->aVlanFilter, u16Vid);
1808 break;
1809 case VNET_CTRL_CMD_VLAN_DEL:
1810 ASMBitClear(pThis->aVlanFilter, u16Vid);
1811 break;
1812 default:
1813 u8Ack = VNET_ERROR;
1814 }
1815
1816 return u8Ack;
1817}
1818
1819
1820/**
1821 * @callback_method_impl{FNVPCIQUEUECALLBACK, The CLT queue}
1822 */
1823static DECLCALLBACK(void) vnetR3QueueControl(PPDMDEVINS pDevIns, PVQUEUE pQueue)
1824{
1825 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1826 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
1827
1828 VQUEUEELEM elem;
1829 while (vqueueGet(pDevIns, &pThis->VPCI, pQueue, &elem))
1830 {
1831 if (elem.cOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1832 {
1833 Log(("%s vnetR3QueueControl: The first 'out' segment is not the header! (%u < 1 || %u < %u).\n",
1834 INSTANCE(pThis), elem.cOut, elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1835 break; /* Skip the element and hope the next one is good. */
1836 }
1837 if ( elem.cIn < 1
1838 || elem.aSegsIn[elem.cIn - 1].cb < sizeof(VNETCTLACK))
1839 {
1840 Log(("%s vnetR3QueueControl: The last 'in' segment is too small to hold the acknowledge! (%u < 1 || %u < %u).\n",
1841 INSTANCE(pThis), elem.cIn, elem.aSegsIn[elem.cIn - 1].cb, sizeof(VNETCTLACK)));
1842 break; /* Skip the element and hope the next one is good. */
1843 }
1844
1845 uint8_t bAck;
1846 VNETCTLHDR CtlHdr;
1847 PDMDevHlpPhysRead(pDevIns, elem.aSegsOut[0].addr, &CtlHdr, sizeof(CtlHdr));
1848 switch (CtlHdr.u8Class)
1849 {
1850 case VNET_CTRL_CLS_RX_MODE:
1851 bAck = vnetR3ControlRx(pDevIns, pThis, pThisCC, &CtlHdr, &elem);
1852 break;
1853 case VNET_CTRL_CLS_MAC:
1854 bAck = vnetR3ControlMac(pDevIns, pThis, &CtlHdr, &elem);
1855 break;
1856 case VNET_CTRL_CLS_VLAN:
1857 bAck = vnetR3ControlVlan(pDevIns, pThis, &CtlHdr, &elem);
1858 break;
1859 default:
1860 bAck = VNET_ERROR;
1861 }
1862 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pThis), CtlHdr.u8Class, bAck));
1863 PDMDevHlpPCIPhysWrite(pDevIns, elem.aSegsIn[elem.cIn - 1].addr, &bAck, sizeof(bAck));
1864
1865 vqueuePut(pDevIns, &pThis->VPCI, pQueue, &elem, sizeof(bAck));
1866 vqueueSync(pDevIns, &pThis->VPCI, pQueue);
1867 }
1868}
1869
1870/* -=-=-=-=- Debug info item -=-=-=-=- */
1871
1872/**
1873 * @callback_method_impl{FNDBGFINFOARGVDEV}
1874 */
1875static DECLCALLBACK(void) vnetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, int cArgs, char **papszArgs)
1876{
1877 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1878 RT_NOREF(cArgs, papszArgs);
1879 vpciR3DumpStateWorker(&pThis->VPCI, pHlp);
1880}
1881
1882
1883/* -=-=-=-=- Saved state -=-=-=-=- */
1884
1885/**
1886 * Saves the configuration.
1887 *
1888 * @param pDevIns The device instance.
1889 * @param pThis The VNET state.
1890 * @param pSSM The handle to the saved state.
1891 */
1892static void vnetR3SaveConfig(PPDMDEVINS pDevIns, PVNETSTATE pThis, PSSMHANDLE pSSM)
1893{
1894 pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->macConfigured, sizeof(pThis->macConfigured));
1895}
1896
1897
1898/**
1899 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1900 */
1901static DECLCALLBACK(int) vnetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1902{
1903 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1904 RT_NOREF(uPass);
1905 vnetR3SaveConfig(pDevIns, pThis, pSSM);
1906 return VINF_SSM_DONT_CALL_AGAIN;
1907}
1908
1909
1910/**
1911 * @callback_method_impl{FNSSMDEVSAVEPREP}
1912 */
1913static DECLCALLBACK(int) vnetR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1914{
1915 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1916 RT_NOREF(pSSM);
1917
1918 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
1919 if (RT_SUCCESS(rc))
1920 vnetCsRxLeave(pThis);
1921 return rc;
1922}
1923
1924
1925/**
1926 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1927 */
1928static DECLCALLBACK(int) vnetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1929{
1930 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1931 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1932
1933 /* Save config first */
1934 vnetR3SaveConfig(pDevIns, pThis, pSSM);
1935
1936 /* Save the common part */
1937 int rc = vpciR3SaveExec(pDevIns, pHlp, &pThis->VPCI, pSSM);
1938 AssertRCReturn(rc, rc);
1939
1940 /* Save device-specific part */
1941 pHlp->pfnSSMPutMem( pSSM, pThis->config.mac.au8, sizeof(pThis->config.mac));
1942 pHlp->pfnSSMPutBool( pSSM, pThis->fPromiscuous);
1943 pHlp->pfnSSMPutBool( pSSM, pThis->fAllMulti);
1944 pHlp->pfnSSMPutU32( pSSM, pThis->cMacFilterEntries);
1945 pHlp->pfnSSMPutMem( pSSM, pThis->aMacFilter, pThis->cMacFilterEntries * sizeof(RTMAC));
1946 rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1947 AssertRCReturn(rc, rc);
1948
1949 Log(("%s State has been saved\n", INSTANCE(pThis)));
1950 return VINF_SUCCESS;
1951}
1952
1953
1954/**
1955 * @callback_method_impl{FNSSMDEVLOADPREP,
1956 * Serializes the receive thread - it may be working inside the critsect. }
1957 */
1958static DECLCALLBACK(int) vnetR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1959{
1960 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1961 RT_NOREF(pSSM);
1962
1963 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
1964 if (RT_SUCCESS(rc))
1965 vnetCsRxLeave(pThis);
1966 return rc;
1967}
1968
1969
1970/**
1971 * @callback_method_impl{FNSSMDEVLOADEXEC}
1972 */
1973static DECLCALLBACK(int) vnetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1974{
1975 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
1976 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
1977 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1978
1979 /* config checks */
1980 RTMAC macConfigured;
1981 int rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured, sizeof(macConfigured));
1982 AssertRCReturn(rc, rc);
1983 if (memcmp(&macConfigured, &pThis->macConfigured, sizeof(macConfigured))
1984 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1985 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pThis), &pThis->macConfigured, &macConfigured));
1986
1987 rc = vpciR3LoadExec(pDevIns, pHlp, &pThis->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1988 AssertRCReturn(rc, rc);
1989
1990 if (uPass == SSM_PASS_FINAL)
1991 {
1992 rc = pHlp->pfnSSMGetMem( pSSM, pThis->config.mac.au8, sizeof(pThis->config.mac));
1993 AssertRCReturn(rc, rc);
1994
1995 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1996 {
1997 pHlp->pfnSSMGetBool(pSSM, &pThis->fPromiscuous);
1998 pHlp->pfnSSMGetBool(pSSM, &pThis->fAllMulti);
1999 pHlp->pfnSSMGetU32(pSSM, &pThis->cMacFilterEntries);
2000 pHlp->pfnSSMGetMem(pSSM, pThis->aMacFilter, pThis->cMacFilterEntries * sizeof(RTMAC));
2001
2002 /* Clear the rest. */
2003 if (pThis->cMacFilterEntries < VNET_MAC_FILTER_LEN)
2004 memset(&pThis->aMacFilter[pThis->cMacFilterEntries],
2005 0,
2006 (VNET_MAC_FILTER_LEN - pThis->cMacFilterEntries) * sizeof(RTMAC));
2007 rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
2008 AssertRCReturn(rc, rc);
2009 }
2010 else
2011 {
2012 pThis->fPromiscuous = true;
2013 pThis->fAllMulti = false;
2014 pThis->cMacFilterEntries = 0;
2015 memset(pThis->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
2016 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
2017 if (pThisCC->pDrv)
2018 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
2019 }
2020 }
2021 Log(("%s State has been restored\n", INSTANCE(pThis)));
2022
2023 return rc;
2024}
2025
2026
2027/**
2028 * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
2029 * loading.}
2030 */
2031static DECLCALLBACK(int) vnetR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2032{
2033 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2034 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
2035 RT_NOREF(pSSM);
2036
2037 if (pThisCC->pDrv)
2038 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulti));
2039 /*
2040 * Indicate link down to the guest OS that all network connections have
2041 * been lost, unless we've been teleported here.
2042 */
2043 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
2044 {
2045 /*
2046 * Since we do not restore the link state, we pretend it is up, so it will be
2047 * lowered by vnetR3TempLinkDown, and the guest will be notified. The actual state
2048 * of the link will be determined later by vnetR3IsConnected in vnetR3LinkUpTimer.
2049 * See @bugref{10096}.
2050 */
2051 pThis->config.uStatus |= VNET_S_LINK_UP;
2052 vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
2053 }
2054
2055 return VINF_SUCCESS;
2056}
2057
2058
2059/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
2060
2061/**
2062 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
2063 */
2064static DECLCALLBACK(void) vnetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2065{
2066 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2067 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
2068 Log(("%s vnetR3Detach:\n", INSTANCE(pThis)));
2069 RT_NOREF(fFlags);
2070
2071 AssertLogRelReturnVoid(iLUN == 0);
2072
2073 VNET_R3_CS_ENTER_RETURN_VOID(pDevIns, pThis);
2074
2075 vnetR3DestroyTxThreadAndEvent(pDevIns, pThis, pThisCC);
2076
2077 /*
2078 * Zero important members.
2079 */
2080 pThisCC->pDrvBase = NULL;
2081 pThisCC->pDrv = NULL;
2082
2083 vnetR3CsLeave(pDevIns, pThis);
2084}
2085
2086
2087/**
2088 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
2089 */
2090static DECLCALLBACK(int) vnetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2091{
2092 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2093 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
2094 RT_NOREF(fFlags);
2095 LogFlow(("%s vnetR3Attach:\n", INSTANCE(pThis)));
2096
2097 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
2098
2099 int rc = vnetR3CsEnter(pDevIns, pThis, VERR_SEM_BUSY);
2100 AssertRCReturn(rc, rc);
2101
2102 /*
2103 * Attach the driver.
2104 */
2105 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->VPCI.IBase, &pThisCC->pDrvBase, "Network Port");
2106 if (RT_SUCCESS(rc))
2107 {
2108 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2109 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2110 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2111
2112 vnetR3CreateTxThreadAndEvent(pDevIns, pThis, pThisCC);
2113 }
2114 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2115 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2116 {
2117 /* This should never happen because this function is not called
2118 * if there is no driver to attach! */
2119 Log(("%s No attached driver!\n", INSTANCE(pThis)));
2120 }
2121
2122 /*
2123 * Temporary set the link down if it was up so that the guest
2124 * will know that we have change the configuration of the
2125 * network card
2126 */
2127 if (RT_SUCCESS(rc))
2128 vnetR3TempLinkDown(pDevIns, pThis, pThisCC);
2129
2130 vnetR3CsLeave(pDevIns, pThis);
2131 return rc;
2132}
2133
2134
2135/**
2136 * @interface_method_impl{PDMDEVREGR3,pfnSuspend}
2137 */
2138static DECLCALLBACK(void) vnetR3Suspend(PPDMDEVINS pDevIns)
2139{
2140 /* Poke thread waiting for buffer space. */
2141 vnetWakeupReceive(pDevIns);
2142}
2143
2144
2145/**
2146 * @interface_method_impl{PDMDEVREGR3,pfnPowerOff}
2147 */
2148static DECLCALLBACK(void) vnetR3PowerOff(PPDMDEVINS pDevIns)
2149{
2150 /* Poke thread waiting for buffer space. */
2151 vnetWakeupReceive(pDevIns);
2152}
2153
2154
2155/**
2156 * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
2157 */
2158static DECLCALLBACK(int) vnetR3Destruct(PPDMDEVINS pDevIns)
2159{
2160 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2161 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2162
2163# ifdef VNET_TX_DELAY
2164 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n", pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
2165# endif
2166
2167 Log(("%s Destroying instance\n", INSTANCE(pThis)));
2168 if (pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
2169 {
2170 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
2171 PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventMoreRxDescAvail);
2172 pThis->hEventMoreRxDescAvail = NIL_SUPSEMEVENT;
2173 }
2174
2175 // if (PDMCritSectIsInitialized(&pThis->csRx))
2176 // PDMR3CritSectDelete(&pThis->csRx);
2177
2178 return vpciR3Term(pDevIns, &pThis->VPCI);
2179}
2180
2181
2182/**
2183 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
2184 */
2185static DECLCALLBACK(int) vnetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2186{
2187 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2188 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2189 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
2190 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2191 int rc;
2192
2193 /*
2194 * Initialize the instance data suffiencently for the destructor not to blow up.
2195 */
2196 RTStrPrintf(pThis->VPCI.szInstance, sizeof(pThis->VPCI.szInstance), "VNet%d", iInstance);
2197 pThisCC->pDevIns = pDevIns;
2198 pThis->hEventMoreRxDescAvail = NIL_SUPSEMEVENT;
2199# ifndef VNET_TX_DELAY
2200 pThis->hTxEvent = NIL_SUPSEMEVENT;
2201 pThisCC->pTxThread = NULL;
2202# endif
2203
2204 /* Initialize state structure */
2205 pThis->u32PktNo = 1;
2206
2207 /* Interfaces */
2208 pThisCC->INetworkDown.pfnWaitReceiveAvail = vnetR3NetworkDown_WaitReceiveAvail;
2209 pThisCC->INetworkDown.pfnReceive = vnetR3NetworkDown_Receive;
2210 pThisCC->INetworkDown.pfnReceiveGso = vnetR3NetworkDown_ReceiveGso;
2211 pThisCC->INetworkDown.pfnXmitPending = vnetR3NetworkDown_XmitPending;
2212
2213 pThisCC->INetworkConfig.pfnGetMac = vnetR3NetworkConfig_GetMac;
2214 pThisCC->INetworkConfig.pfnGetLinkState = vnetR3NetworkConfig_GetLinkState;
2215 pThisCC->INetworkConfig.pfnSetLinkState = vnetR3NetworkConfig_SetLinkState;
2216
2217
2218 /* Do our own locking. */
2219 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
2220 AssertRCReturn(rc, rc);
2221
2222 /*
2223 * Initialize VPCI part.
2224 */
2225 pThisCC->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
2226
2227 rc = vpciR3Init(pDevIns, &pThis->VPCI, &pThisCC->VPCI, VIRTIO_NET_ID, VNET_PCI_CLASS, VNET_N_QUEUES);
2228 AssertRCReturn(rc, rc);
2229
2230 pThisCC->pRxQueue = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 256, vnetR3QueueReceive, "RX ");
2231 pThisCC->pTxQueue = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 256, vnetR3QueueTransmit, "TX ");
2232 pThisCC->pCtlQueue = vpciR3AddQueue(&pThis->VPCI, &pThisCC->VPCI, 16, vnetR3QueueControl, "CTL");
2233 AssertLogRelReturn(pThisCC->pCtlQueue && pThisCC->pTxQueue && pThisCC->pRxQueue, VERR_INTERNAL_ERROR_5);
2234
2235 Log(("%s Constructing new instance\n", INSTANCE(pThis)));
2236
2237 /*
2238 * Validate configuration.
2239 */
2240 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo", "");
2241
2242 /* Get config params */
2243 rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
2244 if (RT_FAILURE(rc))
2245 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
2246
2247 rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
2248 if (RT_FAILURE(rc))
2249 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
2250
2251 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
2252 if (RT_FAILURE(rc))
2253 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
2254 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
2255 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
2256 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2257 Log(("%s Link up delay is set to %u seconds\n", INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
2258
2259 uint32_t uStatNo = iInstance;
2260 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
2261 if (RT_FAILURE(rc))
2262 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
2263
2264 vnetPrintFeatures(pThis, vnetIoCb_GetHostFeatures(&pThis->VPCI), "Device supports the following features");
2265
2266 /* Initialize PCI config space */
2267 memcpy(pThis->config.mac.au8, pThis->macConfigured.au8, sizeof(pThis->config.mac.au8));
2268 pThis->config.uStatus = 0;
2269
2270 /* Initialize critical section. */
2271 // char szTmp[sizeof(pThis->VPCI.szInstance) + 2];
2272 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pThis->VPCI.szInstance);
2273 // rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csRx, szTmp);
2274 // if (RT_FAILURE(rc))
2275 // return rc;
2276
2277 /* Map our ports to IO space. */
2278 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, 0 /*iPciReion*/, VPCI_CONFIG + sizeof(VNetPCIConfig),
2279 vnetIOPortOut, vnetIOPortIn, NULL /*pvUser*/, "VirtioNet", NULL /*paExtDescs*/,
2280 &pThis->hIoPorts);
2281 AssertRCReturn(rc, rc);
2282
2283 /* Register save/restore state handlers. */
2284 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
2285 NULL, vnetR3LiveExec, NULL,
2286 vnetR3SavePrep, vnetR3SaveExec, NULL,
2287 vnetR3LoadPrep, vnetR3LoadExec, vnetR3LoadDone);
2288 AssertRCReturn(rc, rc);
2289
2290 /* Create Link Up Timer */
2291 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetR3LinkUpTimer, NULL,
2292 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
2293 "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
2294 AssertRCReturn(rc, rc);
2295
2296# ifdef VNET_TX_DELAY
2297 /* Create Transmit Delay Timer */
2298 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetR3TxTimer, pThis,
2299 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
2300 "VirtioNet TX Delay", &pThis->hTxTimer);
2301 AssertRCReturn(rc, rc);
2302
2303 pThis->u32i = pThis->u32AvgDiff = pThis->u32MaxDiff = 0;
2304 pThis->u32MinDiff = UINT32_MAX;
2305# endif /* VNET_TX_DELAY */
2306
2307 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->VPCI.IBase, &pThisCC->pDrvBase, "Network Port");
2308 if (RT_SUCCESS(rc))
2309 {
2310 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2311 AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2312 VERR_PDM_MISSING_INTERFACE_BELOW);
2313
2314 vnetR3CreateTxThreadAndEvent(pDevIns, pThis, pThisCC);
2315 }
2316 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2317 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
2318 {
2319 /* No error! */
2320 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pThis)));
2321 }
2322 else
2323 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
2324
2325 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventMoreRxDescAvail);
2326 AssertRCReturn(rc, rc);
2327
2328 ASMCompilerBarrier(); /* paranoia */
2329 rc = vnetIoCb_Reset(pDevIns);
2330 AssertRCReturn(rc, rc);
2331
2332 /*
2333 * Statistics and debug stuff.
2334 * The /Public/ bits are official and used by session info in the GUI.
2335 */
2336 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
2337 "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
2338 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
2339 "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
2340 PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
2341 "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
2342
2343 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
2344 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
2345 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
2346 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
2347 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
2348 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
2349# ifdef VBOX_WITH_STATISTICS
2350 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
2351 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
2352 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
2353 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
2354 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
2355 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
2356 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
2357 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
2358# endif
2359
2360 char szInfo[16];
2361 RTStrPrintf(szInfo, sizeof(szInfo), pDevIns->iInstance ? "vionet%u" : "vionet", pDevIns->iInstance);
2362 PDMDevHlpDBGFInfoRegisterArgv(pDevIns, szInfo, "virtio-net info", vnetR3Info);
2363
2364 return VINF_SUCCESS;
2365}
2366
2367#else /* !IN_RING3 */
2368
2369/**
2370 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
2371 */
2372static DECLCALLBACK(int) vnetRZConstruct(PPDMDEVINS pDevIns)
2373{
2374 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2375 PVNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVNETSTATE);
2376 PVNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVNETSTATECC);
2377
2378 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
2379 AssertRCReturn(rc, rc);
2380
2381 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, vnetIOPortOut, vnetIOPortIn, NULL /*pvUser*/);
2382 AssertRCReturn(rc, rc);
2383
2384 return vpciRZInit(pDevIns, &pThis->VPCI, &pThisCC->VPCI);
2385}
2386
2387#endif /* !IN_RING3 */
2388
2389/**
2390 * The device registration structure.
2391 */
2392const PDMDEVREG g_DeviceVirtioNet =
2393{
2394 /* .u32version = */ PDM_DEVREG_VERSION,
2395 /* .uReserved0 = */ 0,
2396 /* .szName = */ "virtio-net",
2397 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
2398 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
2399 /* .cMaxInstances = */ ~0U,
2400 /* .uSharedVersion = */ 42,
2401 /* .cbInstanceShared = */ sizeof(VNETSTATE),
2402 /* .cbInstanceCC = */ sizeof(VNETSTATECC),
2403 /* .cbInstanceRC = */ sizeof(VNETSTATERC),
2404 /* .cMaxPciDevices = */ 1,
2405 /* .cMaxMsixVectors = */ 0,
2406 /* .pszDescription = */ "Virtio Ethernet.\n",
2407#if defined(IN_RING3)
2408 /* .pszRCMod = */ "VBoxDDRC.rc",
2409 /* .pszR0Mod = */ "VBoxDDR0.r0",
2410 /* .pfnConstruct = */ vnetR3Construct,
2411 /* .pfnDestruct = */ vnetR3Destruct,
2412 /* .pfnRelocate = */ NULL,
2413 /* .pfnMemSetup = */ NULL,
2414 /* .pfnPowerOn = */ NULL,
2415 /* .pfnReset = */ NULL,
2416 /* .pfnSuspend = */ vnetR3Suspend,
2417 /* .pfnResume = */ NULL,
2418 /* .pfnAttach = */ vnetR3Attach,
2419 /* .pfnDetach = */ vnetR3Detach,
2420 /* .pfnQueryInterface = */ NULL,
2421 /* .pfnInitComplete = */ NULL,
2422 /* .pfnPowerOff = */ vnetR3PowerOff,
2423 /* .pfnSoftReset = */ NULL,
2424 /* .pfnReserved0 = */ NULL,
2425 /* .pfnReserved1 = */ NULL,
2426 /* .pfnReserved2 = */ NULL,
2427 /* .pfnReserved3 = */ NULL,
2428 /* .pfnReserved4 = */ NULL,
2429 /* .pfnReserved5 = */ NULL,
2430 /* .pfnReserved6 = */ NULL,
2431 /* .pfnReserved7 = */ NULL,
2432#elif defined(IN_RING0)
2433 /* .pfnEarlyConstruct = */ NULL,
2434 /* .pfnConstruct = */ vnetRZConstruct,
2435 /* .pfnDestruct = */ NULL,
2436 /* .pfnFinalDestruct = */ NULL,
2437 /* .pfnRequest = */ NULL,
2438 /* .pfnReserved0 = */ NULL,
2439 /* .pfnReserved1 = */ NULL,
2440 /* .pfnReserved2 = */ NULL,
2441 /* .pfnReserved3 = */ NULL,
2442 /* .pfnReserved4 = */ NULL,
2443 /* .pfnReserved5 = */ NULL,
2444 /* .pfnReserved6 = */ NULL,
2445 /* .pfnReserved7 = */ NULL,
2446#elif defined(IN_RC)
2447 /* .pfnConstruct = */ vnetRZConstruct,
2448 /* .pfnReserved0 = */ NULL,
2449 /* .pfnReserved1 = */ NULL,
2450 /* .pfnReserved2 = */ NULL,
2451 /* .pfnReserved3 = */ NULL,
2452 /* .pfnReserved4 = */ NULL,
2453 /* .pfnReserved5 = */ NULL,
2454 /* .pfnReserved6 = */ NULL,
2455 /* .pfnReserved7 = */ NULL,
2456#else
2457# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2458#endif
2459 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2460};
2461
2462#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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