VirtualBox

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

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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