VirtualBox

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

Last change on this file since 26597 was 26305, checked in by vboxsync, 15 years ago

pdmnetif.h & users: sketched out a new interface that is intended to be similar to the ring-0 and raw-mode context interfaces (working on driver allocated buffers during xmit).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.6 KB
Line 
1/* $Id: DevVirtioNet.cpp 26305 2010-02-05 19:00:45Z vboxsync $ */
2/** @file
3 * DevVirtioNet - Virtio Network Device
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
24#define VNET_GC_SUPPORT
25
26#include <VBox/pdmdev.h>
27#include <VBox/pdmnetifs.h>
28#include <iprt/semaphore.h>
29#ifdef IN_RING3
30# include <iprt/mem.h>
31# include <iprt/uuid.h>
32#endif /* IN_RING3 */
33#include "../Builtins.h"
34#include "../VirtIO/Virtio.h"
35
36
37#ifndef VBOX_DEVICE_STRUCT_TESTCASE
38
39#define INSTANCE(pState) pState->VPCI.szInstance
40#define IFACE_TO_STATE(pIface, ifaceName) \
41 ((VNETSTATE *)((char*)pIface - RT_OFFSETOF(VNETSTATE, ifaceName)))
42#define STATUS pState->config.uStatus
43
44#ifdef IN_RING3
45
46#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_NET_ID
47#define VNET_PCI_CLASS 0x0200
48#define VNET_N_QUEUES 3
49#define VNET_NAME_FMT "VNet%d"
50
51#if 0
52/* Virtio Block Device */
53#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_BLK_ID
54#define VNET_PCI_CLASS 0x0180
55#define VNET_N_QUEUES 2
56#define VNET_NAME_FMT "VBlk%d"
57#endif
58
59#endif /* IN_RING3 */
60
61/* Forward declarations ******************************************************/
62RT_C_DECLS_BEGIN
63PDMBOTHCBDECL(int) vnetIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
64PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
65RT_C_DECLS_END
66
67#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
68
69
70#define VNET_TX_DELAY 150 /* 150 microseconds */
71#define VNET_MAX_FRAME_SIZE 65536 // TODO: Is it the right limit?
72#define VNET_MAC_FILTER_LEN 32
73#define VNET_MAX_VID (1 << 12)
74
75/* Virtio net features */
76#define VNET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
77#define VNET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
78#define VNET_F_MAC 0x00000020 /* Host has given MAC address. */
79#define VNET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
80#define VNET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
81#define VNET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
82#define VNET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
83#define VNET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
84#define VNET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
85#define VNET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
86#define VNET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
87#define VNET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
88#define VNET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
89#define VNET_F_STATUS 0x00010000 /* virtio_net_config.status available */
90#define VNET_F_CTRL_VQ 0x00020000 /* Control channel available */
91#define VNET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
92#define VNET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
93
94#define VNET_S_LINK_UP 1
95
96
97#ifdef _MSC_VER
98struct VNetPCIConfig
99#else /* !_MSC_VER */
100struct __attribute__ ((__packed__)) VNetPCIConfig
101#endif /* !_MSC_VER */
102{
103 RTMAC mac;
104 uint16_t uStatus;
105};
106AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
107
108/**
109 * Device state structure. Holds the current state of device.
110 *
111 * @extends VPCISTATE
112 * @implements PDMINETWORKDOWN
113 * @implements PDMINETWORKCONFIG
114 */
115struct VNetState_st
116{
117 /* VPCISTATE must be the first member! */
118 VPCISTATE VPCI;
119
120// PDMCRITSECT csRx; /**< Protects RX queue. */
121
122 PDMINETWORKDOWN INetworkDown;
123 PDMINETWORKCONFIG INetworkConfig;
124 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
125 R3PTRTYPE(PPDMINETWORKUP) pDrv; /**< Connector of attached network driver. */
126
127 R3PTRTYPE(PPDMQUEUE) pCanRxQueueR3; /**< Rx wakeup signaller - R3. */
128 R0PTRTYPE(PPDMQUEUE) pCanRxQueueR0; /**< Rx wakeup signaller - R0. */
129 RCPTRTYPE(PPDMQUEUE) pCanRxQueueRC; /**< Rx wakeup signaller - RC. */
130
131#if HC_ARCH_BITS == 64
132 uint32_t padding;
133#endif
134
135 /** transmit buffer */
136 R3PTRTYPE(uint8_t*) pTxBuf;
137 /**< Link Up(/Restore) Timer. */
138 PTMTIMERR3 pLinkUpTimer;
139#ifdef VNET_TX_DELAY
140 /**< Transmit Delay Timer - R3. */
141 PTMTIMERR3 pTxTimerR3;
142 /**< Transmit Delay Timer - R0. */
143 PTMTIMERR0 pTxTimerR0;
144 /**< Transmit Delay Timer - GC. */
145 PTMTIMERRC pTxTimerRC;
146
147#if HC_ARCH_BITS == 64
148 uint32_t padding2;
149#endif
150
151 uint32_t u32i;
152 uint32_t u32AvgDiff;
153 uint32_t u32MinDiff;
154 uint32_t u32MaxDiff;
155 uint64_t u64NanoTS;
156
157#endif /* VNET_TX_DELAY */
158 /** Indicates transmission in progress -- only one thread is allowed. */
159 uint32_t uIsTransmitting;
160
161 /** PCI config area holding MAC address as well as TBD. */
162 struct VNetPCIConfig config;
163 /** MAC address obtained from the configuration. */
164 RTMAC macConfigured;
165 /** True if physical cable is attached in configuration. */
166 bool fCableConnected;
167
168 /** Number of packet being sent/received to show in debug log. */
169 uint32_t u32PktNo;
170
171 /** N/A: */
172 bool volatile fMaybeOutOfSpace;
173
174 /** Promiscuous mode -- RX filter accepts all packets. */
175 bool fPromiscuous;
176 /** AllMulti mode -- RX filter accepts all multicast packets. */
177 bool fAllMulti;
178 /** The number of actually used slots in aMacTable. */
179 uint32_t nMacFilterEntries;
180 /** Array of MAC addresses accepted by RX filter. */
181 RTMAC aMacFilter[VNET_MAC_FILTER_LEN];
182 /** Bit array of VLAN filter, one bit per VLAN ID. */
183 uint8_t aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
184
185#if HC_ARCH_BITS != 64
186 uint32_t padding3;
187#endif
188
189 R3PTRTYPE(PVQUEUE) pRxQueue;
190 R3PTRTYPE(PVQUEUE) pTxQueue;
191 R3PTRTYPE(PVQUEUE) pCtlQueue;
192 /* Receive-blocking-related fields ***************************************/
193
194 /** EMT: Gets signalled when more RX descriptors become available. */
195 RTSEMEVENT hEventMoreRxDescAvail;
196
197 /* Statistic fields ******************************************************/
198
199 STAMCOUNTER StatReceiveBytes;
200 STAMCOUNTER StatTransmitBytes;
201#if defined(VBOX_WITH_STATISTICS)
202 STAMPROFILE StatReceive;
203 STAMPROFILE StatReceiveStore;
204 STAMPROFILEADV StatTransmit;
205 STAMPROFILE StatTransmitSend;
206 STAMPROFILE StatRxOverflow;
207 STAMCOUNTER StatRxOverflowWakeup;
208#endif /* VBOX_WITH_STATISTICS */
209
210};
211typedef struct VNetState_st VNETSTATE;
212typedef VNETSTATE *PVNETSTATE;
213
214#ifndef VBOX_DEVICE_STRUCT_TESTCASE
215
216#define VNETHDR_GSO_NONE 0
217
218struct VNetHdr
219{
220 uint8_t u8Flags;
221 uint8_t u8GSOType;
222 uint16_t u16HdrLen;
223 uint16_t u16GSOSize;
224 uint16_t u16CSumStart;
225 uint16_t u16CSumOffset;
226};
227typedef struct VNetHdr VNETHDR;
228typedef VNETHDR *PVNETHDR;
229AssertCompileSize(VNETHDR, 10);
230
231AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
232
233#define VNET_OK 0
234#define VNET_ERROR 1
235typedef uint8_t VNETCTLACK;
236
237#define VNET_CTRL_CLS_RX_MODE 0
238#define VNET_CTRL_CMD_RX_MODE_PROMISC 0
239#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
240
241#define VNET_CTRL_CLS_MAC 1
242#define VNET_CTRL_CMD_MAC_TABLE_SET 0
243
244#define VNET_CTRL_CLS_VLAN 2
245#define VNET_CTRL_CMD_VLAN_ADD 0
246#define VNET_CTRL_CMD_VLAN_DEL 1
247
248
249struct VNetCtlHdr
250{
251 uint8_t u8Class;
252 uint8_t u8Command;
253};
254typedef struct VNetCtlHdr VNETCTLHDR;
255typedef VNETCTLHDR *PVNETCTLHDR;
256AssertCompileSize(VNETCTLHDR, 2);
257
258DECLINLINE(int) vnetCsEnter(PVNETSTATE pState, int rcBusy)
259{
260 return vpciCsEnter(&pState->VPCI, rcBusy);
261}
262
263DECLINLINE(void) vnetCsLeave(PVNETSTATE pState)
264{
265 vpciCsLeave(&pState->VPCI);
266}
267
268DECLINLINE(int) vnetCsRxEnter(PVNETSTATE pState, int rcBusy)
269{
270 // STAM_PROFILE_START(&pState->CTXSUFF(StatCsRx), a);
271 // int rc = PDMCritSectEnter(&pState->csRx, rcBusy);
272 // STAM_PROFILE_STOP(&pState->CTXSUFF(StatCsRx), a);
273 // return rc;
274 return VINF_SUCCESS;
275}
276
277DECLINLINE(void) vnetCsRxLeave(PVNETSTATE pState)
278{
279 // PDMCritSectLeave(&pState->csRx);
280}
281
282/**
283 * Dump a packet to debug log.
284 *
285 * @param pState The device state structure.
286 * @param cpPacket The packet.
287 * @param cb The size of the packet.
288 * @param cszText A string denoting direction of packet transfer.
289 */
290DECLINLINE(void) vnetPacketDump(PVNETSTATE pState, const uint8_t *cpPacket, size_t cb, const char *cszText)
291{
292#ifdef DEBUG
293 Log(("%s %s packet #%d (%d bytes):\n",
294 INSTANCE(pState), cszText, ++pState->u32PktNo, cb));
295 //Log3(("%.*Rhxd\n", cb, cpPacket));
296#endif
297}
298
299
300
301PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
302{
303 /* We support:
304 * - Host-provided MAC address
305 * - Link status reporting in config space
306 * - Control queue
307 * - RX mode setting
308 * - MAC filter table
309 * - VLAN filter
310 */
311 return VNET_F_MAC
312 | VNET_F_STATUS
313 | VNET_F_CTRL_VQ
314 | VNET_F_CTRL_RX
315 | VNET_F_CTRL_VLAN;
316}
317
318PDMBOTHCBDECL(uint32_t) vnetGetHostMinimalFeatures(void *pvState)
319{
320 return VNET_F_MAC;
321}
322
323PDMBOTHCBDECL(void) vnetSetHostFeatures(void *pvState, uint32_t uFeatures)
324{
325 // TODO: Nothing to do here yet
326 VNETSTATE *pState = (VNETSTATE *)pvState;
327 LogFlow(("%s vnetSetHostFeatures: uFeatures=%x\n", INSTANCE(pState), uFeatures));
328}
329
330PDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
331{
332 VNETSTATE *pState = (VNETSTATE *)pvState;
333 if (port + cb > sizeof(struct VNetPCIConfig))
334 {
335 Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
336 return VERR_IOM_IOPORT_UNUSED;
337 }
338 memcpy(data, ((uint8_t*)&pState->config) + port, cb);
339 return VINF_SUCCESS;
340}
341
342PDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
343{
344 VNETSTATE *pState = (VNETSTATE *)pvState;
345 if (port + cb > sizeof(struct VNetPCIConfig))
346 {
347 Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
348 if (port < sizeof(struct VNetPCIConfig))
349 memcpy(((uint8_t*)&pState->config) + port, data,
350 sizeof(struct VNetPCIConfig) - port);
351 return VINF_SUCCESS;
352 }
353 memcpy(((uint8_t*)&pState->config) + port, data, cb);
354 return VINF_SUCCESS;
355}
356
357/**
358 * Hardware reset. Revert all registers to initial values.
359 *
360 * @param pState The device state structure.
361 */
362PDMBOTHCBDECL(void) vnetReset(void *pvState)
363{
364 VNETSTATE *pState = (VNETSTATE*)pvState;
365 Log(("%s Reset triggered\n", INSTANCE(pState)));
366
367 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
368 if (RT_UNLIKELY(rc != VINF_SUCCESS))
369 {
370 LogRel(("vnetReset failed to enter RX critical section!\n"));
371 return;
372 }
373 vpciReset(&pState->VPCI);
374 vnetCsRxLeave(pState);
375
376 // TODO: Implement reset
377 if (pState->fCableConnected)
378 STATUS = VNET_S_LINK_UP;
379 else
380 STATUS = 0;
381 /*
382 * By default we pass all packets up since the older guests cannot control
383 * virtio mode.
384 */
385 pState->fPromiscuous = true;
386 pState->fAllMulti = false;
387 pState->nMacFilterEntries = 0;
388 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
389 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
390 pState->uIsTransmitting = 0;
391}
392
393#ifdef IN_RING3
394/**
395 * Wakeup the RX thread.
396 */
397static void vnetWakeupReceive(PPDMDEVINS pDevIns)
398{
399 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
400 if ( pState->fMaybeOutOfSpace
401 && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
402 {
403 STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
404 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
405 RTSemEventSignal(pState->hEventMoreRxDescAvail);
406 }
407}
408
409/**
410 * Link Up Timer handler.
411 *
412 * @param pDevIns Pointer to device instance structure.
413 * @param pTimer Pointer to the timer.
414 * @param pvUser NULL.
415 * @thread EMT
416 */
417static DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
418{
419 VNETSTATE *pState = (VNETSTATE *)pvUser;
420
421 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
422 if (RT_UNLIKELY(rc != VINF_SUCCESS))
423 return;
424 STATUS |= VNET_S_LINK_UP;
425 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
426 vnetWakeupReceive(pDevIns);
427 vnetCsLeave(pState);
428}
429
430
431
432
433/**
434 * Handler for the wakeup signaller queue.
435 */
436static DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
437{
438 vnetWakeupReceive(pDevIns);
439 return true;
440}
441
442#endif /* IN_RING3 */
443
444/**
445 * This function is called when the driver becomes ready.
446 *
447 * @param pState The device state structure.
448 */
449PDMBOTHCBDECL(void) vnetReady(void *pvState)
450{
451 VNETSTATE *pState = (VNETSTATE*)pvState;
452 Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(pState)));
453#ifdef IN_RING3
454 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
455#else
456 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pState->CTX_SUFF(pCanRxQueue));
457 if (pItem)
458 PDMQueueInsert(pState->CTX_SUFF(pCanRxQueue), pItem);
459#endif
460}
461
462/**
463 * Port I/O Handler for IN operations.
464 *
465 * @returns VBox status code.
466 *
467 * @param pDevIns The device instance.
468 * @param pvUser Pointer to the device state structure.
469 * @param port Port number used for the IN operation.
470 * @param pu32 Where to store the result.
471 * @param cb Number of bytes read.
472 * @thread EMT
473 */
474PDMBOTHCBDECL(int) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
475 RTIOPORT port, uint32_t *pu32, unsigned cb)
476{
477 return vpciIOPortIn(pDevIns, pvUser, port, pu32, cb,
478 vnetGetHostFeatures,
479 vnetGetConfig);
480}
481
482
483/**
484 * Port I/O Handler for OUT operations.
485 *
486 * @returns VBox status code.
487 *
488 * @param pDevIns The device instance.
489 * @param pvUser User argument.
490 * @param Port Port number used for the IN operation.
491 * @param u32 The value to output.
492 * @param cb The value size in bytes.
493 * @thread EMT
494 */
495PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
496 RTIOPORT port, uint32_t u32, unsigned cb)
497{
498 return vpciIOPortOut(pDevIns, pvUser, port, u32, cb,
499 vnetGetHostMinimalFeatures,
500 vnetGetHostFeatures,
501 vnetSetHostFeatures,
502 vnetReset,
503 vnetReady,
504 vnetSetConfig);
505}
506
507
508#ifdef IN_RING3
509
510/**
511 * Check if the device can receive data now.
512 * This must be called before the pfnRecieve() method is called.
513 *
514 * @remarks As a side effect this function enables queue notification
515 * if it cannot receive because the queue is empty.
516 * It disables notification if it can receive.
517 *
518 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
519 * @param pInterface Pointer to the interface structure containing the called function pointer.
520 * @thread RX
521 */
522static int vnetCanReceive(VNETSTATE *pState)
523{
524 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
525 AssertRCReturn(rc, rc);
526
527 LogFlow(("%s vnetCanReceive\n", INSTANCE(pState)));
528 if (!(pState->VPCI.uStatus & VPCI_STATUS_DRV_OK))
529 rc = VERR_NET_NO_BUFFER_SPACE;
530 else if (!vqueueIsReady(&pState->VPCI, pState->pRxQueue))
531 rc = VERR_NET_NO_BUFFER_SPACE;
532 else if (vqueueIsEmpty(&pState->VPCI, pState->pRxQueue))
533 {
534 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, true);
535 rc = VERR_NET_NO_BUFFER_SPACE;
536 }
537 else
538 {
539 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, false);
540 rc = VINF_SUCCESS;
541 }
542
543 LogFlow(("%s vnetCanReceive -> %Rrc\n", INSTANCE(pState), rc));
544 vnetCsRxLeave(pState);
545 return rc;
546}
547
548static DECLCALLBACK(int) vnetWaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
549{
550 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkDown);
551 LogFlow(("%s vnetWaitReceiveAvail(cMillies=%u)\n", INSTANCE(pState), cMillies));
552 int rc = vnetCanReceive(pState);
553
554 if (RT_SUCCESS(rc))
555 return VINF_SUCCESS;
556 if (RT_UNLIKELY(cMillies == 0))
557 return VERR_NET_NO_BUFFER_SPACE;
558
559 rc = VERR_INTERRUPTED;
560 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
561 STAM_PROFILE_START(&pState->StatRxOverflow, a);
562
563 VMSTATE enmVMState;
564 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
565 || enmVMState == VMSTATE_RUNNING_LS))
566 {
567 int rc2 = vnetCanReceive(pState);
568 if (RT_SUCCESS(rc2))
569 {
570 rc = VINF_SUCCESS;
571 break;
572 }
573 Log(("%s vnetWaitReceiveAvail: waiting cMillies=%u...\n",
574 INSTANCE(pState), cMillies));
575 RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
576 }
577 STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
578 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
579
580 LogFlow(("%s vnetWaitReceiveAvail -> %d\n", INSTANCE(pState), rc));
581 return rc;
582}
583
584
585/**
586 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
587 */
588static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
589{
590 VNETSTATE *pThis = IFACE_TO_STATE(pInterface, VPCI.IBase);
591 Assert(&pThis->VPCI.IBase == pInterface);
592
593 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
594 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
595 return vpciQueryInterface(pInterface, pszIID);
596}
597
598/**
599 * Returns true if it is a broadcast packet.
600 *
601 * @returns true if destination address indicates broadcast.
602 * @param pvBuf The ethernet packet.
603 */
604DECLINLINE(bool) vnetIsBroadcast(const void *pvBuf)
605{
606 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
607 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
608}
609
610/**
611 * Returns true if it is a multicast packet.
612 *
613 * @remarks returns true for broadcast packets as well.
614 * @returns true if destination address indicates multicast.
615 * @param pvBuf The ethernet packet.
616 */
617DECLINLINE(bool) vnetIsMulticast(const void *pvBuf)
618{
619 return (*(char*)pvBuf) & 1;
620}
621
622/**
623 * Determines if the packet is to be delivered to upper layer.
624 *
625 * @returns true if packet is intended for this node.
626 * @param pState Pointer to the state structure.
627 * @param pvBuf The ethernet packet.
628 * @param cb Number of bytes available in the packet.
629 */
630static bool vnetAddressFilter(PVNETSTATE pState, const void *pvBuf, size_t cb)
631{
632 if (pState->fPromiscuous)
633 return true;
634
635 /* Ignore everything outside of our VLANs */
636 uint16_t *u16Ptr = (uint16_t*)pvBuf;
637 /* Compare TPID with VLAN Ether Type */
638 if ( u16Ptr[6] == RT_H2BE_U16(0x8100)
639 && !ASMBitTest(pState->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
640 return false;
641
642 if (vnetIsBroadcast(pvBuf))
643 return true;
644
645 if (pState->fAllMulti && vnetIsMulticast(pvBuf))
646 return true;
647
648 if (!memcmp(pState->config.mac.au8, pvBuf, sizeof(RTMAC)))
649 return true;
650
651 for (unsigned i = 0; i < pState->nMacFilterEntries; i++)
652 if (!memcmp(&pState->aMacFilter[i], pvBuf, sizeof(RTMAC)))
653 return true;
654
655 return false;
656}
657
658/**
659 * Pad and store received packet.
660 *
661 * @remarks Make sure that the packet appears to upper layer as one coming
662 * from real Ethernet: pad it and insert FCS.
663 *
664 * @returns VBox status code.
665 * @param pState The device state structure.
666 * @param pvBuf The available data.
667 * @param cb Number of bytes available in the buffer.
668 * @thread RX
669 */
670static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb)
671{
672 VNETHDR hdr;
673
674 hdr.u8Flags = 0;
675 hdr.u8GSOType = VNETHDR_GSO_NONE;
676
677 vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
678
679 unsigned int uOffset = 0;
680 for (unsigned int nElem = 0; uOffset < cb; nElem++)
681 {
682 VQUEUEELEM elem;
683 unsigned int nSeg = 0, uElemSize = 0;
684
685 if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
686 {
687 Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
688 return VERR_INTERNAL_ERROR;
689 }
690
691 if (elem.nIn < 1)
692 {
693 Log(("%s vnetHandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pState)));
694 return VERR_INTERNAL_ERROR;
695 }
696
697 if (nElem == 0)
698 {
699 /* The very first segment of the very first element gets the header. */
700 if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
701 {
702 Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
703 return VERR_INTERNAL_ERROR;
704 }
705
706 elem.aSegsIn[nSeg++].pv = &hdr;
707 uElemSize += sizeof(VNETHDR);
708 }
709
710 while (nSeg < elem.nIn && uOffset < cb)
711 {
712 unsigned int uSize = RT_MIN(elem.aSegsIn[nSeg].cb, cb - uOffset);
713 elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
714 uOffset += uSize;
715 uElemSize += uSize;
716 }
717 STAM_PROFILE_START(&pState->StatReceiveStore, a);
718 vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize);
719 STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
720 }
721 vqueueSync(&pState->VPCI, pState->pRxQueue);
722
723 return VINF_SUCCESS;
724}
725
726/**
727 * Receive data from the network.
728 *
729 * @returns VBox status code.
730 * @param pInterface Pointer to the interface structure containing the called function pointer.
731 * @param pvBuf The available data.
732 * @param cb Number of bytes available in the buffer.
733 * @thread RX
734 */
735static DECLCALLBACK(int) vnetReceive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
736{
737 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkDown);
738
739 Log2(("%s vnetReceive: pvBuf=%p cb=%u\n", INSTANCE(pState), pvBuf, cb));
740 int rc = vnetCanReceive(pState);
741 if (RT_FAILURE(rc))
742 return rc;
743
744 /* Drop packets if VM is not running or cable is disconnected. */
745 VMSTATE enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns));
746 if (( enmVMState != VMSTATE_RUNNING
747 && enmVMState != VMSTATE_RUNNING_LS)
748 || !(STATUS & VNET_S_LINK_UP))
749 return VINF_SUCCESS;
750
751 STAM_PROFILE_START(&pState->StatReceive, a);
752 vpciSetReadLed(&pState->VPCI, true);
753 if (vnetAddressFilter(pState, pvBuf, cb))
754 {
755 rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
756 if (RT_SUCCESS(rc))
757 {
758 rc = vnetHandleRxPacket(pState, pvBuf, cb);
759 STAM_REL_COUNTER_ADD(&pState->StatReceiveBytes, cb);
760 vnetCsRxLeave(pState);
761 }
762 }
763 vpciSetReadLed(&pState->VPCI, false);
764 STAM_PROFILE_STOP(&pState->StatReceive, a);
765 return rc;
766}
767
768/**
769 * Gets the current Media Access Control (MAC) address.
770 *
771 * @returns VBox status code.
772 * @param pInterface Pointer to the interface structure containing the called function pointer.
773 * @param pMac Where to store the MAC address.
774 * @thread EMT
775 */
776static DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
777{
778 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
779 memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
780 return VINF_SUCCESS;
781}
782
783/**
784 * Gets the new link state.
785 *
786 * @returns The current link state.
787 * @param pInterface Pointer to the interface structure containing the called function pointer.
788 * @thread EMT
789 */
790static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
791{
792 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
793 if (STATUS & VNET_S_LINK_UP)
794 return PDMNETWORKLINKSTATE_UP;
795 return PDMNETWORKLINKSTATE_DOWN;
796}
797
798
799/**
800 * Sets the new link state.
801 *
802 * @returns VBox status code.
803 * @param pInterface Pointer to the interface structure containing the called function pointer.
804 * @param enmState The new link state
805 */
806static DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
807{
808 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
809 bool fOldUp = !!(STATUS & VNET_S_LINK_UP);
810 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
811
812 if (fNewUp != fOldUp)
813 {
814 if (fNewUp)
815 {
816 Log(("%s Link is up\n", INSTANCE(pState)));
817 STATUS |= VNET_S_LINK_UP;
818 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
819 }
820 else
821 {
822 Log(("%s Link is down\n", INSTANCE(pState)));
823 STATUS &= ~VNET_S_LINK_UP;
824 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
825 }
826 if (pState->pDrv)
827 pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
828 }
829 return VINF_SUCCESS;
830}
831
832static DECLCALLBACK(void) vnetQueueReceive(void *pvState, PVQUEUE pQueue)
833{
834 VNETSTATE *pState = (VNETSTATE*)pvState;
835 Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pState)));
836 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
837}
838
839static DECLCALLBACK(void) vnetTransmitPendingPackets(PVNETSTATE pState, PVQUEUE pQueue)
840{
841 /*
842 * Only one thread is allowed to transmit at a time, others should skip
843 * transmission as the packets will be picked up by the transmitting
844 * thread.
845 */
846 if (!ASMAtomicCmpXchgU32(&pState->uIsTransmitting, 1, 0))
847 return;
848
849 if ((pState->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
850 {
851 Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n",
852 INSTANCE(pState), pState->VPCI.uStatus));
853 return;
854 }
855
856 Log3(("%s vnetTransmitPendingPackets: About to trasmit %d pending packets\n", INSTANCE(pState),
857 vringReadAvailIndex(&pState->VPCI, &pState->pTxQueue->VRing) - pState->pTxQueue->uNextAvailIndex));
858
859 vpciSetWriteLed(&pState->VPCI, true);
860
861 VQUEUEELEM elem;
862 while (vqueueGet(&pState->VPCI, pQueue, &elem))
863 {
864 unsigned int uOffset = 0;
865 if (elem.nOut < 2 || elem.aSegsOut[0].cb != sizeof(VNETHDR))
866 {
867 Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
868 INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, sizeof(VNETHDR)));
869 break; /* For now we simply ignore the header, but it must be there anyway! */
870 }
871 else
872 {
873 STAM_PROFILE_ADV_START(&pState->StatTransmit, a);
874 /* Assemble a complete frame. */
875 for (unsigned int i = 1; i < elem.nOut && uOffset < VNET_MAX_FRAME_SIZE; i++)
876 {
877 unsigned int uSize = elem.aSegsOut[i].cb;
878 if (uSize > VNET_MAX_FRAME_SIZE - uOffset)
879 {
880 Log(("%s vnetQueueTransmit: Packet is too big (>64k), truncating...\n", INSTANCE(pState)));
881 uSize = VNET_MAX_FRAME_SIZE - uOffset;
882 }
883 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
884 pState->pTxBuf + uOffset, uSize);
885 uOffset += uSize;
886 }
887 if (pState->pDrv)
888 {
889 vnetPacketDump(pState, pState->pTxBuf, uOffset, "--> Outgoing");
890
891 STAM_PROFILE_START(&pState->StatTransmitSend, a);
892 int rc = pState->pDrv->pfnSendDeprecated(pState->pDrv, pState->pTxBuf, uOffset);
893 STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
894 STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
895 }
896 }
897 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
898 vqueueSync(&pState->VPCI, pQueue);
899 STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
900 }
901 vpciSetWriteLed(&pState->VPCI, false);
902
903 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
904}
905
906#ifdef VNET_TX_DELAY
907static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
908{
909 VNETSTATE *pState = (VNETSTATE*)pvState;
910
911 if (TMTimerIsActive(pState->CTX_SUFF(pTxTimer)))
912 {
913 int rc = TMTimerStop(pState->CTX_SUFF(pTxTimer));
914 Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
915 "re-enable notification and flush TX queue\n", INSTANCE(pState)));
916 vnetTransmitPendingPackets(pState, pQueue);
917 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
918 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
919 else
920 {
921 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
922 vnetCsLeave(pState);
923 }
924 }
925 else
926 {
927 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
928 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
929 else
930 {
931 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, false);
932 TMTimerSetMicro(pState->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
933 pState->u64NanoTS = RTTimeNanoTS();
934 vnetCsLeave(pState);
935 }
936 }
937}
938
939/**
940 * Transmit Delay Timer handler.
941 *
942 * @remarks We only get here when the timer expires.
943 *
944 * @param pDevIns Pointer to device instance structure.
945 * @param pTimer Pointer to the timer.
946 * @param pvUser NULL.
947 * @thread EMT
948 */
949static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
950{
951 VNETSTATE *pState = (VNETSTATE*)pvUser;
952
953 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pState->u64NanoTS)/1000);
954 if (u32MicroDiff < pState->u32MinDiff)
955 pState->u32MinDiff = u32MicroDiff;
956 if (u32MicroDiff > pState->u32MaxDiff)
957 pState->u32MaxDiff = u32MicroDiff;
958 pState->u32AvgDiff = (pState->u32AvgDiff * pState->u32i + u32MicroDiff) / (pState->u32i + 1);
959 pState->u32i++;
960 Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
961 u32MicroDiff, pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
962
963// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pState)));
964 vnetTransmitPendingPackets(pState, pState->pTxQueue);
965 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
966 {
967 LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
968 return;
969 }
970 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
971 vnetCsLeave(pState);
972}
973
974#else /* !VNET_TX_DELAY */
975static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
976{
977 VNETSTATE *pState = (VNETSTATE*)pvState;
978
979 vnetTransmitPendingPackets(pState, pQueue);
980}
981#endif /* !VNET_TX_DELAY */
982
983static uint8_t vnetControlRx(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
984{
985 uint8_t u8Ack = VNET_OK;
986 uint8_t fOn;
987 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
988 pElem->aSegsOut[1].addr,
989 &fOn, sizeof(fOn));
990 Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pState), pCtlHdr->u8Command, fOn));
991 switch (pCtlHdr->u8Command)
992 {
993 case VNET_CTRL_CMD_RX_MODE_PROMISC:
994 pState->fPromiscuous = !!fOn;
995 break;
996 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
997 pState->fAllMulti = !!fOn;
998 break;
999 default:
1000 u8Ack = VNET_ERROR;
1001 }
1002
1003 return u8Ack;
1004}
1005
1006static uint8_t vnetControlMac(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1007{
1008 uint32_t nMacs = 0;
1009
1010 if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1011 || pElem->nOut != 3
1012 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1013 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1014 {
1015 Log(("%s vnetControlMac: Segment layout is wrong "
1016 "(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pState),
1017 pCtlHdr->u8Command, pElem->nOut,
1018 pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1019 return VNET_ERROR;
1020 }
1021
1022 /* Load unicast addresses */
1023 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1024 pElem->aSegsOut[1].addr,
1025 &nMacs, sizeof(nMacs));
1026
1027 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1028 {
1029 Log(("%s vnetControlMac: The unicast mac segment is too small "
1030 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[1].cb));
1031 return VNET_ERROR;
1032 }
1033
1034 if (nMacs > VNET_MAC_FILTER_LEN)
1035 {
1036 Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
1037 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1038 pState->fPromiscuous = true;
1039 }
1040 else
1041 {
1042 if (nMacs)
1043 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1044 pElem->aSegsOut[1].addr + sizeof(nMacs),
1045 pState->aMacFilter, nMacs * sizeof(RTMAC));
1046 pState->nMacFilterEntries = nMacs;
1047#ifdef DEBUG
1048 Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pState)));
1049 for(unsigned i = 0; i < nMacs; i++)
1050 Log((" %RTmac\n", &pState->aMacFilter[i]));
1051#endif /* DEBUG */
1052 }
1053
1054 /* Load multicast addresses */
1055 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1056 pElem->aSegsOut[2].addr,
1057 &nMacs, sizeof(nMacs));
1058
1059 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1060 {
1061 Log(("%s vnetControlMac: The multicast mac segment is too small "
1062 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[2].cb));
1063 return VNET_ERROR;
1064 }
1065
1066 if (nMacs > VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1067 {
1068 Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
1069 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1070 pState->fAllMulti = true;
1071 }
1072 else
1073 {
1074 if (nMacs)
1075 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1076 pElem->aSegsOut[2].addr + sizeof(nMacs),
1077 &pState->aMacFilter[pState->nMacFilterEntries],
1078 nMacs * sizeof(RTMAC));
1079#ifdef DEBUG
1080 Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pState)));
1081 for(unsigned i = 0; i < nMacs; i++)
1082 Log((" %RTmac\n",
1083 &pState->aMacFilter[i+pState->nMacFilterEntries]));
1084#endif /* DEBUG */
1085 pState->nMacFilterEntries += nMacs;
1086 }
1087
1088 return VNET_OK;
1089}
1090
1091static uint8_t vnetControlVlan(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1092{
1093 uint8_t u8Ack = VNET_OK;
1094 uint16_t u16Vid;
1095
1096 if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1097 {
1098 Log(("%s vnetControlVlan: Segment layout is wrong "
1099 "(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pState),
1100 pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
1101 return VNET_ERROR;
1102 }
1103
1104 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1105 pElem->aSegsOut[1].addr,
1106 &u16Vid, sizeof(u16Vid));
1107
1108 if (u16Vid >= VNET_MAX_VID)
1109 {
1110 Log(("%s vnetControlVlan: VLAN ID is out of range "
1111 "(VID=%u)\n", INSTANCE(pState), u16Vid));
1112 return VNET_ERROR;
1113 }
1114
1115 Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pState),
1116 pCtlHdr->u8Command, u16Vid));
1117
1118 switch (pCtlHdr->u8Command)
1119 {
1120 case VNET_CTRL_CMD_VLAN_ADD:
1121 ASMBitSet(pState->aVlanFilter, u16Vid);
1122 break;
1123 case VNET_CTRL_CMD_VLAN_DEL:
1124 ASMBitClear(pState->aVlanFilter, u16Vid);
1125 break;
1126 default:
1127 u8Ack = VNET_ERROR;
1128 }
1129
1130 return u8Ack;
1131}
1132
1133
1134static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1135{
1136 VNETSTATE *pState = (VNETSTATE*)pvState;
1137 uint8_t u8Ack;
1138 VQUEUEELEM elem;
1139 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1140 {
1141 unsigned int uOffset = 0;
1142 if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1143 {
1144 Log(("%s vnetQueueControl: The first 'out' segment is not the "
1145 "header! (%u < 1 || %u < %u).\n", INSTANCE(pState), elem.nOut,
1146 elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1147 break; /* Skip the element and hope the next one is good. */
1148 }
1149 else if ( elem.nIn < 1
1150 || elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
1151 {
1152 Log(("%s vnetQueueControl: The last 'in' segment is too small "
1153 "to hold the acknowledge! (%u < 1 || %u < %u).\n",
1154 INSTANCE(pState), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
1155 sizeof(VNETCTLACK)));
1156 break; /* Skip the element and hope the next one is good. */
1157 }
1158 else
1159 {
1160 VNETCTLHDR CtlHdr;
1161 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1162 elem.aSegsOut[0].addr,
1163 &CtlHdr, sizeof(CtlHdr));
1164 switch (CtlHdr.u8Class)
1165 {
1166 case VNET_CTRL_CLS_RX_MODE:
1167 u8Ack = vnetControlRx(pState, &CtlHdr, &elem);
1168 break;
1169 case VNET_CTRL_CLS_MAC:
1170 u8Ack = vnetControlMac(pState, &CtlHdr, &elem);
1171 break;
1172 case VNET_CTRL_CLS_VLAN:
1173 u8Ack = vnetControlVlan(pState, &CtlHdr, &elem);
1174 break;
1175 default:
1176 u8Ack = VNET_ERROR;
1177 }
1178 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pState),
1179 CtlHdr.u8Class, u8Ack));
1180 PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns),
1181 elem.aSegsIn[elem.nIn - 1].addr,
1182 &u8Ack, sizeof(u8Ack));
1183 }
1184 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(u8Ack));
1185 vqueueSync(&pState->VPCI, pQueue);
1186 }
1187}
1188
1189/**
1190 * Saves the configuration.
1191 *
1192 * @param pState The VNET state.
1193 * @param pSSM The handle to the saved state.
1194 */
1195static void vnetSaveConfig(VNETSTATE *pState, PSSMHANDLE pSSM)
1196{
1197 SSMR3PutMem(pSSM, &pState->macConfigured, sizeof(pState->macConfigured));
1198}
1199
1200/**
1201 * Live save - save basic configuration.
1202 *
1203 * @returns VBox status code.
1204 * @param pDevIns The device instance.
1205 * @param pSSM The handle to the saved state.
1206 * @param uPass
1207 */
1208static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1209{
1210 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1211 vnetSaveConfig(pState, pSSM);
1212 return VINF_SSM_DONT_CALL_AGAIN;
1213}
1214
1215/**
1216 * Prepares for state saving.
1217 *
1218 * @returns VBox status code.
1219 * @param pDevIns The device instance.
1220 * @param pSSM The handle to the saved state.
1221 */
1222static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1223{
1224 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1225
1226 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1227 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1228 return rc;
1229 vnetCsRxLeave(pState);
1230 return VINF_SUCCESS;
1231}
1232
1233/**
1234 * Saves the state of device.
1235 *
1236 * @returns VBox status code.
1237 * @param pDevIns The device instance.
1238 * @param pSSM The handle to the saved state.
1239 */
1240static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1241{
1242 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1243
1244 /* Save config first */
1245 vnetSaveConfig(pState, pSSM);
1246
1247 /* Save the common part */
1248 int rc = vpciSaveExec(&pState->VPCI, pSSM);
1249 AssertRCReturn(rc, rc);
1250 /* Save device-specific part */
1251 rc = SSMR3PutMem( pSSM, pState->config.mac.au8, sizeof(pState->config.mac));
1252 AssertRCReturn(rc, rc);
1253 rc = SSMR3PutBool(pSSM, pState->fPromiscuous);
1254 AssertRCReturn(rc, rc);
1255 rc = SSMR3PutBool(pSSM, pState->fAllMulti);
1256 AssertRCReturn(rc, rc);
1257 rc = SSMR3PutU32( pSSM, pState->nMacFilterEntries);
1258 AssertRCReturn(rc, rc);
1259 rc = SSMR3PutMem( pSSM, pState->aMacFilter,
1260 pState->nMacFilterEntries * sizeof(RTMAC));
1261 AssertRCReturn(rc, rc);
1262 rc = SSMR3PutMem( pSSM, pState->aVlanFilter, sizeof(pState->aVlanFilter));
1263 AssertRCReturn(rc, rc);
1264 Log(("%s State has been saved\n", INSTANCE(pState)));
1265 return VINF_SUCCESS;
1266}
1267
1268
1269/**
1270 * Serializes the receive thread, it may be working inside the critsect.
1271 *
1272 * @returns VBox status code.
1273 * @param pDevIns The device instance.
1274 * @param pSSM The handle to the saved state.
1275 */
1276static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1277{
1278 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1279
1280 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1281 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1282 return rc;
1283 vnetCsRxLeave(pState);
1284 return VINF_SUCCESS;
1285}
1286
1287/* Takes down the link temporarily if it's current status is up.
1288 *
1289 * This is used during restore and when replumbing the network link.
1290 *
1291 * The temporary link outage is supposed to indicate to the OS that all network
1292 * connections have been lost and that it for instance is appropriate to
1293 * renegotiate any DHCP lease.
1294 *
1295 * @param pThis The PCNet instance data.
1296 */
1297static void vnetTempLinkDown(PVNETSTATE pState)
1298{
1299 if (STATUS & VNET_S_LINK_UP)
1300 {
1301 STATUS &= ~VNET_S_LINK_UP;
1302 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1303 /* Restore the link back in 5 seconds. */
1304 int rc = TMTimerSetMillies(pState->pLinkUpTimer, 5000);
1305 AssertRC(rc);
1306 }
1307}
1308
1309
1310/**
1311 * Restore previously saved state of device.
1312 *
1313 * @returns VBox status code.
1314 * @param pDevIns The device instance.
1315 * @param pSSM The handle to the saved state.
1316 * @param uVersion The data unit version number.
1317 * @param uPass The data pass.
1318 */
1319static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1320{
1321 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1322 int rc;
1323
1324 /* config checks */
1325 RTMAC macConfigured;
1326 rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
1327 AssertRCReturn(rc, rc);
1328 if (memcmp(&macConfigured, &pState->macConfigured, sizeof(macConfigured))
1329 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1330 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pState), &pState->macConfigured, &macConfigured));
1331
1332 rc = vpciLoadExec(&pState->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1333 AssertRCReturn(rc, rc);
1334
1335 if (uPass == SSM_PASS_FINAL)
1336 {
1337 rc = SSMR3GetMem( pSSM, pState->config.mac.au8,
1338 sizeof(pState->config.mac));
1339 AssertRCReturn(rc, rc);
1340 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1341 {
1342 rc = SSMR3GetBool(pSSM, &pState->fPromiscuous);
1343 AssertRCReturn(rc, rc);
1344 rc = SSMR3GetBool(pSSM, &pState->fAllMulti);
1345 AssertRCReturn(rc, rc);
1346 rc = SSMR3GetU32(pSSM, &pState->nMacFilterEntries);
1347 AssertRCReturn(rc, rc);
1348 rc = SSMR3GetMem(pSSM, pState->aMacFilter,
1349 pState->nMacFilterEntries * sizeof(RTMAC));
1350 AssertRCReturn(rc, rc);
1351 /* Clear the rest. */
1352 if (pState->nMacFilterEntries < VNET_MAC_FILTER_LEN)
1353 memset(&pState->aMacFilter[pState->nMacFilterEntries],
1354 0,
1355 (VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1356 * sizeof(RTMAC));
1357 rc = SSMR3GetMem(pSSM, pState->aVlanFilter,
1358 sizeof(pState->aVlanFilter));
1359 AssertRCReturn(rc, rc);
1360 }
1361 else
1362 {
1363 pState->fPromiscuous = true;
1364 pState->fAllMulti = false;
1365 pState->nMacFilterEntries = 0;
1366 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1367 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
1368 }
1369 }
1370
1371 return rc;
1372}
1373
1374/**
1375 * Link status adjustments after loading.
1376 *
1377 * @returns VBox status code.
1378 * @param pDevIns The device instance.
1379 * @param pSSM The handle to the saved state.
1380 */
1381static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1382{
1383 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1384
1385 /*
1386 * Indicate link down to the guest OS that all network connections have
1387 * been lost, unless we've been teleported here.
1388 */
1389 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1390 vnetTempLinkDown(pState);
1391
1392 return VINF_SUCCESS;
1393}
1394
1395/**
1396 * Map PCI I/O region.
1397 *
1398 * @return VBox status code.
1399 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
1400 * @param iRegion The region number.
1401 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
1402 * I/O port, else it's a physical address.
1403 * This address is *NOT* relative to pci_mem_base like earlier!
1404 * @param cb Region size.
1405 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
1406 * @thread EMT
1407 */
1408static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
1409 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
1410{
1411 int rc;
1412 VNETSTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VNETSTATE*);
1413
1414 if (enmType != PCI_ADDRESS_SPACE_IO)
1415 {
1416 /* We should never get here */
1417 AssertMsgFailed(("Invalid PCI address space param in map callback"));
1418 return VERR_INTERNAL_ERROR;
1419 }
1420
1421 pState->VPCI.addrIOPort = (RTIOPORT)GCPhysAddress;
1422 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1423 cb, 0, vnetIOPortOut, vnetIOPortIn,
1424 NULL, NULL, "VirtioNet");
1425#ifdef VNET_GC_SUPPORT
1426 AssertRCReturn(rc, rc);
1427 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1428 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1429 NULL, NULL, "VirtioNet");
1430 AssertRCReturn(rc, rc);
1431 rc = PDMDevHlpIOPortRegisterRC(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1432 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1433 NULL, NULL, "VirtioNet");
1434#endif
1435 AssertRC(rc);
1436 return rc;
1437}
1438
1439/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
1440
1441#ifdef VBOX_DYNAMIC_NET_ATTACH
1442
1443/**
1444 * Detach notification.
1445 *
1446 * One port on the network card has been disconnected from the network.
1447 *
1448 * @param pDevIns The device instance.
1449 * @param iLUN The logical unit which is being detached.
1450 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1451 */
1452static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1453{
1454 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1455 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1456
1457 AssertLogRelReturnVoid(iLUN == 0);
1458
1459 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1460 if (RT_FAILURE(rc))
1461 {
1462 LogRel(("vnetDetach failed to enter critical section!\n"));
1463 return;
1464 }
1465
1466 /*
1467 * Zero some important members.
1468 */
1469 pState->pDrvBase = NULL;
1470 pState->pDrv = NULL;
1471
1472 vnetCsLeave(pState);
1473}
1474
1475
1476/**
1477 * Attach the Network attachment.
1478 *
1479 * One port on the network card has been connected to a network.
1480 *
1481 * @returns VBox status code.
1482 * @param pDevIns The device instance.
1483 * @param iLUN The logical unit which is being attached.
1484 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1485 *
1486 * @remarks This code path is not used during construction.
1487 */
1488static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1489{
1490 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1491 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1492
1493 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1494
1495 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1496 if (RT_FAILURE(rc))
1497 {
1498 LogRel(("vnetAttach failed to enter critical section!\n"));
1499 return rc;
1500 }
1501
1502 /*
1503 * Attach the driver.
1504 */
1505 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1506 if (RT_SUCCESS(rc))
1507 {
1508 if (rc == VINF_NAT_DNS)
1509 {
1510#ifdef RT_OS_LINUX
1511 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1512 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"));
1513#else
1514 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1515 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"));
1516#endif
1517 }
1518 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1519 AssertMsgStmt(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1520 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1521 }
1522 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1523 Log(("%s No attached driver!\n", INSTANCE(pState)));
1524
1525
1526 /*
1527 * Temporary set the link down if it was up so that the guest
1528 * will know that we have change the configuration of the
1529 * network card
1530 */
1531 if (RT_SUCCESS(rc))
1532 vnetTempLinkDown(pState);
1533
1534 vnetCsLeave(pState);
1535 return rc;
1536
1537}
1538
1539#endif /* VBOX_DYNAMIC_NET_ATTACH */
1540
1541/**
1542 * @copydoc FNPDMDEVSUSPEND
1543 */
1544static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1545{
1546 /* Poke thread waiting for buffer space. */
1547 vnetWakeupReceive(pDevIns);
1548}
1549
1550/**
1551 * @copydoc FNPDMDEVPOWEROFF
1552 */
1553static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1554{
1555 /* Poke thread waiting for buffer space. */
1556 vnetWakeupReceive(pDevIns);
1557}
1558
1559/**
1560 * Device relocation callback.
1561 *
1562 * When this callback is called the device instance data, and if the
1563 * device have a GC component, is being relocated, or/and the selectors
1564 * have been changed. The device must use the chance to perform the
1565 * necessary pointer relocations and data updates.
1566 *
1567 * Before the GC code is executed the first time, this function will be
1568 * called with a 0 delta so GC pointer calculations can be one in one place.
1569 *
1570 * @param pDevIns Pointer to the device instance.
1571 * @param offDelta The relocation delta relative to the old location.
1572 *
1573 * @remark A relocation CANNOT fail.
1574 */
1575static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1576{
1577 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1578 vpciRelocate(pDevIns, offDelta);
1579 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1580#ifdef VNET_TX_DELAY
1581 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1582#endif /* VNET_TX_DELAY */
1583 // TBD
1584}
1585
1586/**
1587 * Destruct a device instance.
1588 *
1589 * We need to free non-VM resources only.
1590 *
1591 * @returns VBox status.
1592 * @param pDevIns The device instance data.
1593 * @thread EMT
1594 */
1595static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1596{
1597 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1598 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1599
1600 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
1601 pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1602 Log(("%s Destroying instance\n", INSTANCE(pState)));
1603 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1604 {
1605 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1606 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1607 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1608 }
1609
1610 if (pState->pTxBuf)
1611 {
1612 RTMemFree(pState->pTxBuf);
1613 pState->pTxBuf = NULL;
1614 }
1615 // if (PDMCritSectIsInitialized(&pState->csRx))
1616 // PDMR3CritSectDelete(&pState->csRx);
1617
1618 return vpciDestruct(&pState->VPCI);
1619}
1620
1621/**
1622 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1623 */
1624static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1625{
1626 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1627 int rc;
1628 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1629
1630 /* Initialize PCI part first. */
1631 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1632 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance,
1633 VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
1634 VNET_PCI_CLASS, VNET_N_QUEUES);
1635 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive, "RX ");
1636 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit, "TX ");
1637 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl, "CTL");
1638
1639 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1640
1641 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1642
1643 /*
1644 * Validate configuration.
1645 */
1646 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "LineSpeed\0"))
1647 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1648 N_("Invalid configuration for VirtioNet device"));
1649
1650 /* Get config params */
1651 rc = CFGMR3QueryBytes(pCfg, "MAC", pState->macConfigured.au8,
1652 sizeof(pState->macConfigured));
1653 if (RT_FAILURE(rc))
1654 return PDMDEV_SET_ERROR(pDevIns, rc,
1655 N_("Configuration error: Failed to get MAC address"));
1656 rc = CFGMR3QueryBool(pCfg, "CableConnected", &pState->fCableConnected);
1657 if (RT_FAILURE(rc))
1658 return PDMDEV_SET_ERROR(pDevIns, rc,
1659 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1660
1661 /* Initialize PCI config space */
1662 memcpy(pState->config.mac.au8, pState->macConfigured.au8, sizeof(pState->config.mac.au8));
1663 pState->config.uStatus = 0;
1664
1665 /* Initialize state structure */
1666 pState->u32PktNo = 1;
1667
1668 /* Interfaces */
1669 pState->INetworkDown.pfnWaitReceiveAvail = vnetWaitReceiveAvail;
1670 pState->INetworkDown.pfnReceive = vnetReceive;
1671 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1672 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1673 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1674
1675 pState->pTxBuf = (uint8_t *)RTMemAllocZ(VNET_MAX_FRAME_SIZE);
1676 AssertMsgReturn(pState->pTxBuf,
1677 ("Cannot allocate TX buffer for virtio-net device\n"), VERR_NO_MEMORY);
1678
1679 /* Initialize critical section. */
1680 // char szTmp[sizeof(pState->VPCI.szInstance) + 2];
1681 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
1682 // rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, szTmp);
1683 // if (RT_FAILURE(rc))
1684 // return rc;
1685
1686 /* Map our ports to IO space. */
1687 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
1688 VPCI_CONFIG + sizeof(VNetPCIConfig),
1689 PCI_ADDRESS_SPACE_IO, vnetMap);
1690 if (RT_FAILURE(rc))
1691 return rc;
1692
1693
1694 /* Register save/restore state handlers. */
1695 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
1696 NULL, vnetLiveExec, NULL,
1697 vnetSavePrep, vnetSaveExec, NULL,
1698 vnetLoadPrep, vnetLoadExec, vnetLoadDone);
1699 if (RT_FAILURE(rc))
1700 return rc;
1701
1702 /* Create the RX notifier signaller. */
1703 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
1704 vnetCanRxQueueConsumer, true, "VNet-Rcv", &pState->pCanRxQueueR3);
1705 if (RT_FAILURE(rc))
1706 return rc;
1707 pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
1708 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1709
1710 /* Create Link Up Timer */
1711 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
1712 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1713 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
1714 if (RT_FAILURE(rc))
1715 return rc;
1716
1717#ifdef VNET_TX_DELAY
1718 /* Create Transmit Delay Timer */
1719 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pState,
1720 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1721 "VirtioNet TX Delay Timer", &pState->pTxTimerR3);
1722 if (RT_FAILURE(rc))
1723 return rc;
1724 pState->pTxTimerR0 = TMTimerR0Ptr(pState->pTxTimerR3);
1725 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1726
1727 pState->u32i = pState->u32AvgDiff = pState->u32MaxDiff = 0;
1728 pState->u32MinDiff = ~0;
1729#endif /* VNET_TX_DELAY */
1730
1731 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1732 if (RT_SUCCESS(rc))
1733 {
1734 if (rc == VINF_NAT_DNS)
1735 {
1736 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1737 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"));
1738 }
1739 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1740 AssertMsgReturn(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1741 VERR_PDM_MISSING_INTERFACE_BELOW);
1742 }
1743 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1744 {
1745 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
1746 }
1747 else
1748 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
1749
1750 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
1751 if (RT_FAILURE(rc))
1752 return rc;
1753
1754 vnetReset(pState);
1755
1756 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
1757 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
1758#if defined(VBOX_WITH_STATISTICS)
1759 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
1760 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
1761 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
1762 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
1763 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
1764 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
1765#endif /* VBOX_WITH_STATISTICS */
1766
1767 return VINF_SUCCESS;
1768}
1769
1770/**
1771 * The device registration structure.
1772 */
1773const PDMDEVREG g_DeviceVirtioNet =
1774{
1775 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
1776 PDM_DEVREG_VERSION,
1777 /* Device name. */
1778 "virtio-net",
1779 /* Name of guest context module (no path).
1780 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1781 "VBoxDDGC.gc",
1782 /* Name of ring-0 module (no path).
1783 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1784 "VBoxDDR0.r0",
1785 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
1786 * remain unchanged from registration till VM destruction. */
1787 "Virtio Ethernet.\n",
1788
1789 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
1790#ifdef VNET_GC_SUPPORT
1791 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1792#else
1793 PDM_DEVREG_FLAGS_DEFAULT_BITS,
1794#endif
1795 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
1796 PDM_DEVREG_CLASS_NETWORK,
1797 /* Maximum number of instances (per VM). */
1798 8,
1799 /* Size of the instance data. */
1800 sizeof(VNETSTATE),
1801
1802 /* Construct instance - required. */
1803 vnetConstruct,
1804 /* Destruct instance - optional. */
1805 vnetDestruct,
1806 /* Relocation command - optional. */
1807 vnetRelocate,
1808 /* I/O Control interface - optional. */
1809 NULL,
1810 /* Power on notification - optional. */
1811 NULL,
1812 /* Reset notification - optional. */
1813 NULL,
1814 /* Suspend notification - optional. */
1815 vnetSuspend,
1816 /* Resume notification - optional. */
1817 NULL,
1818#ifdef VBOX_DYNAMIC_NET_ATTACH
1819 /* Attach command - optional. */
1820 vnetAttach,
1821 /* Detach notification - optional. */
1822 vnetDetach,
1823#else /* !VBOX_DYNAMIC_NET_ATTACH */
1824 /* Attach command - optional. */
1825 NULL,
1826 /* Detach notification - optional. */
1827 NULL,
1828#endif /* !VBOX_DYNAMIC_NET_ATTACH */
1829 /* Query a LUN base interface - optional. */
1830 NULL,
1831 /* Init complete notification - optional. */
1832 NULL,
1833 /* Power off notification - optional. */
1834 vnetPowerOff,
1835 /* pfnSoftReset */
1836 NULL,
1837 /* u32VersionEnd */
1838 PDM_DEVREG_VERSION
1839};
1840
1841#endif /* IN_RING3 */
1842#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