VirtualBox

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

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

Dev/VirtioNet: (bugref:8651) Fixed connectivity loss due to stuck TX thread on cable disconnection.

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