VirtualBox

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

Last change on this file since 81708 was 81708, checked in by vboxsync, 5 years ago

DevVirtioNet: Replaced the CanRxQueue and hEventMoreRxDescAvail semaphore with a SUPSEMEVENT semaphore. bugref:9218

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

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