VirtualBox

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

Last change on this file since 61541 was 57901, checked in by vboxsync, 9 years ago

Devices/Virtio: semantic fixes.

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