VirtualBox

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

Last change on this file since 87766 was 87760, checked in by vboxsync, 4 years ago

VMM/TM,VMM/DevHlp: Require flag on timers that are to be used in ring-0 (and while refactoring a counte flag to check that all timers have been checked). Removed obsolete timer device helpers. bugref:9943

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