VirtualBox

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

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

#3987: Virtio: Fixed the race for TX queue notification state.

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