VirtualBox

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

Last change on this file since 69922 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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