VirtualBox

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

Last change on this file since 63547 was 63478, checked in by vboxsync, 8 years ago

Devices: warnings (clang)

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