VirtualBox

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

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

virtio-net: alignment

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

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