VirtualBox

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

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

s/VMMGC.gc/VMMRC.rc/g s/VBoxDDGC.gc/VBoxDDRC.rc/g s/VBoxDD2GC.gc/VBoxDD2RC.rc/g

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