VirtualBox

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

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

*: s/%Vuuid/%RTuuid/g; s/%Vrc/%Rrc/g

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