VirtualBox

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

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

DevVirtioNet: More paranoia.

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