VirtualBox

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

Last change on this file since 95288 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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