VirtualBox

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

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

Devices/Network: Call pfnBeginXmit and pfnEndXmit.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette