VirtualBox

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

Last change on this file since 72919 was 72474, checked in by vboxsync, 6 years ago

More Windows build fixes.

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