VirtualBox

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

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

virtio-net: removed TX buffer along with redundant memcpy call

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