VirtualBox

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

Last change on this file since 33000 was 31767, checked in by vboxsync, 14 years ago

build fix

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

© 2023 Oracle
ContactPrivacy policyTerms of Use