VirtualBox

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

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

Applied patch for (bugref:9098) Dev/VirtioNet: no sends on disconnected link; do not raise interrupts until driver ready.

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