VirtualBox

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

Last change on this file since 63288 was 62962, checked in by vboxsync, 9 years ago

Devices: warnings

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