VirtualBox

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

最後變更 在這個檔案從31765是 31765,由 vboxsync 提交於 15 年 前

virtio-net: fixed offset modification bug

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 68.5 KB
 
1/* $Id: DevVirtioNet.cpp 31765 2010-08-18 14:08:34Z 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.alldomusa.eu.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 {
990 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
991 ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
992 elem.aSegsOut[i].cb);
993 uOffset += elem.aSegsOut[i].cb;
994 }
995 pSgBuf->cbUsed = uSize;
996 vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
997 if (pGso)
998 STAM_REL_COUNTER_INC(&pState->StatTransmitGSO);
999 else if (Hdr.u8Flags & VNETHDR_F_NEEDS_CSUM)
1000 {
1001 STAM_REL_COUNTER_INC(&pState->StatTransmitCSum);
1002 /*
1003 * This is not GSO frame but checksum offloading is requested.
1004 */
1005 vnetCompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize,
1006 Hdr.u16CSumStart, Hdr.u16CSumOffset);
1007 }
1008
1009 rc = pState->pDrv->pfnSendBuf(pState->pDrv, pSgBuf, false);
1010 }
1011 else
1012 LogRel(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
1013
1014 STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
1015 STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
1016 }
1017 }
1018 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1019 vqueueSync(&pState->VPCI, pQueue);
1020 STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
1021 }
1022 vpciSetWriteLed(&pState->VPCI, false);
1023
1024 if (pDrv)
1025 pDrv->pfnEndXmit(pDrv);
1026 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
1027}
1028
1029/**
1030 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
1031 */
1032static DECLCALLBACK(void) vnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
1033{
1034 VNETSTATE *pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
1035 vnetTransmitPendingPackets(pThis, pThis->pTxQueue, false /*fOnWorkerThread*/);
1036}
1037
1038#ifdef VNET_TX_DELAY
1039
1040static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1041{
1042 VNETSTATE *pState = (VNETSTATE*)pvState;
1043
1044 if (TMTimerIsActive(pState->CTX_SUFF(pTxTimer)))
1045 {
1046 int rc = TMTimerStop(pState->CTX_SUFF(pTxTimer));
1047 Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
1048 "re-enable notification and flush TX queue\n", INSTANCE(pState)));
1049 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1050 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1051 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1052 else
1053 {
1054 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1055 vnetCsLeave(pState);
1056 }
1057 }
1058 else
1059 {
1060 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1061 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1062 else
1063 {
1064 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, false);
1065 TMTimerSetMicro(pState->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
1066 pState->u64NanoTS = RTTimeNanoTS();
1067 vnetCsLeave(pState);
1068 }
1069 }
1070}
1071
1072/**
1073 * Transmit Delay Timer handler.
1074 *
1075 * @remarks We only get here when the timer expires.
1076 *
1077 * @param pDevIns Pointer to device instance structure.
1078 * @param pTimer Pointer to the timer.
1079 * @param pvUser NULL.
1080 * @thread EMT
1081 */
1082static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1083{
1084 VNETSTATE *pState = (VNETSTATE*)pvUser;
1085
1086 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pState->u64NanoTS)/1000);
1087 if (u32MicroDiff < pState->u32MinDiff)
1088 pState->u32MinDiff = u32MicroDiff;
1089 if (u32MicroDiff > pState->u32MaxDiff)
1090 pState->u32MaxDiff = u32MicroDiff;
1091 pState->u32AvgDiff = (pState->u32AvgDiff * pState->u32i + u32MicroDiff) / (pState->u32i + 1);
1092 pState->u32i++;
1093 Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
1094 u32MicroDiff, pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1095
1096// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pState)));
1097 vnetTransmitPendingPackets(pState, pState->pTxQueue, false /*fOnWorkerThread*/);
1098 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1099 {
1100 LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
1101 return;
1102 }
1103 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1104 vnetCsLeave(pState);
1105}
1106
1107#else /* !VNET_TX_DELAY */
1108
1109static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1110{
1111 VNETSTATE *pState = (VNETSTATE*)pvState;
1112
1113 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1114}
1115
1116#endif /* !VNET_TX_DELAY */
1117
1118static uint8_t vnetControlRx(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1119{
1120 uint8_t u8Ack = VNET_OK;
1121 uint8_t fOn, fDrvWasPromisc = pState->fPromiscuous | pState->fAllMulti;
1122 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1123 pElem->aSegsOut[1].addr,
1124 &fOn, sizeof(fOn));
1125 Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pState), pCtlHdr->u8Command, fOn));
1126 switch (pCtlHdr->u8Command)
1127 {
1128 case VNET_CTRL_CMD_RX_MODE_PROMISC:
1129 pState->fPromiscuous = !!fOn;
1130 break;
1131 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
1132 pState->fAllMulti = !!fOn;
1133 break;
1134 default:
1135 u8Ack = VNET_ERROR;
1136 }
1137 if (fDrvWasPromisc != (pState->fPromiscuous | pState->fAllMulti) && pState->pDrv)
1138 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1139 (pState->fPromiscuous | pState->fAllMulti));
1140
1141 return u8Ack;
1142}
1143
1144static uint8_t vnetControlMac(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1145{
1146 uint32_t nMacs = 0;
1147
1148 if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1149 || pElem->nOut != 3
1150 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1151 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1152 {
1153 Log(("%s vnetControlMac: Segment layout is wrong "
1154 "(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pState),
1155 pCtlHdr->u8Command, pElem->nOut,
1156 pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1157 return VNET_ERROR;
1158 }
1159
1160 /* Load unicast addresses */
1161 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1162 pElem->aSegsOut[1].addr,
1163 &nMacs, sizeof(nMacs));
1164
1165 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1166 {
1167 Log(("%s vnetControlMac: The unicast mac segment is too small "
1168 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[1].cb));
1169 return VNET_ERROR;
1170 }
1171
1172 if (nMacs > VNET_MAC_FILTER_LEN)
1173 {
1174 Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
1175 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1176 pState->fPromiscuous = true;
1177 }
1178 else
1179 {
1180 if (nMacs)
1181 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1182 pElem->aSegsOut[1].addr + sizeof(nMacs),
1183 pState->aMacFilter, nMacs * sizeof(RTMAC));
1184 pState->nMacFilterEntries = nMacs;
1185#ifdef DEBUG
1186 Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pState)));
1187 for(unsigned i = 0; i < nMacs; i++)
1188 Log((" %RTmac\n", &pState->aMacFilter[i]));
1189#endif /* DEBUG */
1190 }
1191
1192 /* Load multicast addresses */
1193 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1194 pElem->aSegsOut[2].addr,
1195 &nMacs, sizeof(nMacs));
1196
1197 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1198 {
1199 Log(("%s vnetControlMac: The multicast mac segment is too small "
1200 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[2].cb));
1201 return VNET_ERROR;
1202 }
1203
1204 if (nMacs > VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1205 {
1206 Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
1207 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1208 pState->fAllMulti = true;
1209 }
1210 else
1211 {
1212 if (nMacs)
1213 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1214 pElem->aSegsOut[2].addr + sizeof(nMacs),
1215 &pState->aMacFilter[pState->nMacFilterEntries],
1216 nMacs * sizeof(RTMAC));
1217#ifdef DEBUG
1218 Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pState)));
1219 for(unsigned i = 0; i < nMacs; i++)
1220 Log((" %RTmac\n",
1221 &pState->aMacFilter[i+pState->nMacFilterEntries]));
1222#endif /* DEBUG */
1223 pState->nMacFilterEntries += nMacs;
1224 }
1225
1226 return VNET_OK;
1227}
1228
1229static uint8_t vnetControlVlan(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1230{
1231 uint8_t u8Ack = VNET_OK;
1232 uint16_t u16Vid;
1233
1234 if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1235 {
1236 Log(("%s vnetControlVlan: Segment layout is wrong "
1237 "(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pState),
1238 pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
1239 return VNET_ERROR;
1240 }
1241
1242 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1243 pElem->aSegsOut[1].addr,
1244 &u16Vid, sizeof(u16Vid));
1245
1246 if (u16Vid >= VNET_MAX_VID)
1247 {
1248 Log(("%s vnetControlVlan: VLAN ID is out of range "
1249 "(VID=%u)\n", INSTANCE(pState), u16Vid));
1250 return VNET_ERROR;
1251 }
1252
1253 Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pState),
1254 pCtlHdr->u8Command, u16Vid));
1255
1256 switch (pCtlHdr->u8Command)
1257 {
1258 case VNET_CTRL_CMD_VLAN_ADD:
1259 ASMBitSet(pState->aVlanFilter, u16Vid);
1260 break;
1261 case VNET_CTRL_CMD_VLAN_DEL:
1262 ASMBitClear(pState->aVlanFilter, u16Vid);
1263 break;
1264 default:
1265 u8Ack = VNET_ERROR;
1266 }
1267
1268 return u8Ack;
1269}
1270
1271
1272static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1273{
1274 VNETSTATE *pState = (VNETSTATE*)pvState;
1275 uint8_t u8Ack;
1276 VQUEUEELEM elem;
1277 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1278 {
1279 unsigned int uOffset = 0;
1280 if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1281 {
1282 Log(("%s vnetQueueControl: The first 'out' segment is not the "
1283 "header! (%u < 1 || %u < %u).\n", INSTANCE(pState), elem.nOut,
1284 elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1285 break; /* Skip the element and hope the next one is good. */
1286 }
1287 else if ( elem.nIn < 1
1288 || elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
1289 {
1290 Log(("%s vnetQueueControl: The last 'in' segment is too small "
1291 "to hold the acknowledge! (%u < 1 || %u < %u).\n",
1292 INSTANCE(pState), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
1293 sizeof(VNETCTLACK)));
1294 break; /* Skip the element and hope the next one is good. */
1295 }
1296 else
1297 {
1298 VNETCTLHDR CtlHdr;
1299 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1300 elem.aSegsOut[0].addr,
1301 &CtlHdr, sizeof(CtlHdr));
1302 switch (CtlHdr.u8Class)
1303 {
1304 case VNET_CTRL_CLS_RX_MODE:
1305 u8Ack = vnetControlRx(pState, &CtlHdr, &elem);
1306 break;
1307 case VNET_CTRL_CLS_MAC:
1308 u8Ack = vnetControlMac(pState, &CtlHdr, &elem);
1309 break;
1310 case VNET_CTRL_CLS_VLAN:
1311 u8Ack = vnetControlVlan(pState, &CtlHdr, &elem);
1312 break;
1313 default:
1314 u8Ack = VNET_ERROR;
1315 }
1316 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pState),
1317 CtlHdr.u8Class, u8Ack));
1318 PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns),
1319 elem.aSegsIn[elem.nIn - 1].addr,
1320 &u8Ack, sizeof(u8Ack));
1321 }
1322 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(u8Ack));
1323 vqueueSync(&pState->VPCI, pQueue);
1324 }
1325}
1326
1327/**
1328 * Saves the configuration.
1329 *
1330 * @param pState The VNET state.
1331 * @param pSSM The handle to the saved state.
1332 */
1333static void vnetSaveConfig(VNETSTATE *pState, PSSMHANDLE pSSM)
1334{
1335 SSMR3PutMem(pSSM, &pState->macConfigured, sizeof(pState->macConfigured));
1336}
1337
1338/**
1339 * Live save - save basic configuration.
1340 *
1341 * @returns VBox status code.
1342 * @param pDevIns The device instance.
1343 * @param pSSM The handle to the saved state.
1344 * @param uPass
1345 */
1346static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1347{
1348 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1349 vnetSaveConfig(pState, pSSM);
1350 return VINF_SSM_DONT_CALL_AGAIN;
1351}
1352
1353/**
1354 * Prepares for state saving.
1355 *
1356 * @returns VBox status code.
1357 * @param pDevIns The device instance.
1358 * @param pSSM The handle to the saved state.
1359 */
1360static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1361{
1362 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1363
1364 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1365 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1366 return rc;
1367 vnetCsRxLeave(pState);
1368 return VINF_SUCCESS;
1369}
1370
1371/**
1372 * Saves the state of device.
1373 *
1374 * @returns VBox status code.
1375 * @param pDevIns The device instance.
1376 * @param pSSM The handle to the saved state.
1377 */
1378static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1379{
1380 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1381
1382 /* Save config first */
1383 vnetSaveConfig(pState, pSSM);
1384
1385 /* Save the common part */
1386 int rc = vpciSaveExec(&pState->VPCI, pSSM);
1387 AssertRCReturn(rc, rc);
1388 /* Save device-specific part */
1389 rc = SSMR3PutMem( pSSM, pState->config.mac.au8, sizeof(pState->config.mac));
1390 AssertRCReturn(rc, rc);
1391 rc = SSMR3PutBool(pSSM, pState->fPromiscuous);
1392 AssertRCReturn(rc, rc);
1393 rc = SSMR3PutBool(pSSM, pState->fAllMulti);
1394 AssertRCReturn(rc, rc);
1395 rc = SSMR3PutU32( pSSM, pState->nMacFilterEntries);
1396 AssertRCReturn(rc, rc);
1397 rc = SSMR3PutMem( pSSM, pState->aMacFilter,
1398 pState->nMacFilterEntries * sizeof(RTMAC));
1399 AssertRCReturn(rc, rc);
1400 rc = SSMR3PutMem( pSSM, pState->aVlanFilter, sizeof(pState->aVlanFilter));
1401 AssertRCReturn(rc, rc);
1402 Log(("%s State has been saved\n", INSTANCE(pState)));
1403 return VINF_SUCCESS;
1404}
1405
1406
1407/**
1408 * Serializes the receive thread, it may be working inside the critsect.
1409 *
1410 * @returns VBox status code.
1411 * @param pDevIns The device instance.
1412 * @param pSSM The handle to the saved state.
1413 */
1414static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1415{
1416 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1417
1418 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1419 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1420 return rc;
1421 vnetCsRxLeave(pState);
1422 return VINF_SUCCESS;
1423}
1424
1425/**
1426 * Takes down the link temporarily if it's current status is up.
1427 *
1428 * This is used during restore and when replumbing the network link.
1429 *
1430 * The temporary link outage is supposed to indicate to the OS that all network
1431 * connections have been lost and that it for instance is appropriate to
1432 * renegotiate any DHCP lease.
1433 *
1434 * @param pThis The PCNet instance data.
1435 */
1436static void vnetTempLinkDown(PVNETSTATE pState)
1437{
1438 if (STATUS & VNET_S_LINK_UP)
1439 {
1440 STATUS &= ~VNET_S_LINK_UP;
1441 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1442 /* Restore the link back in 5 seconds. */
1443 int rc = TMTimerSetMillies(pState->pLinkUpTimer, 5000);
1444 AssertRC(rc);
1445 }
1446}
1447
1448
1449/**
1450 * Restore previously saved state of device.
1451 *
1452 * @returns VBox status code.
1453 * @param pDevIns The device instance.
1454 * @param pSSM The handle to the saved state.
1455 * @param uVersion The data unit version number.
1456 * @param uPass The data pass.
1457 */
1458static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1459{
1460 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1461 int rc;
1462
1463 /* config checks */
1464 RTMAC macConfigured;
1465 rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
1466 AssertRCReturn(rc, rc);
1467 if (memcmp(&macConfigured, &pState->macConfigured, sizeof(macConfigured))
1468 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1469 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pState), &pState->macConfigured, &macConfigured));
1470
1471 rc = vpciLoadExec(&pState->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1472 AssertRCReturn(rc, rc);
1473
1474 if (uPass == SSM_PASS_FINAL)
1475 {
1476 rc = SSMR3GetMem( pSSM, pState->config.mac.au8,
1477 sizeof(pState->config.mac));
1478 AssertRCReturn(rc, rc);
1479 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1480 {
1481 rc = SSMR3GetBool(pSSM, &pState->fPromiscuous);
1482 AssertRCReturn(rc, rc);
1483 rc = SSMR3GetBool(pSSM, &pState->fAllMulti);
1484 AssertRCReturn(rc, rc);
1485 rc = SSMR3GetU32(pSSM, &pState->nMacFilterEntries);
1486 AssertRCReturn(rc, rc);
1487 rc = SSMR3GetMem(pSSM, pState->aMacFilter,
1488 pState->nMacFilterEntries * sizeof(RTMAC));
1489 AssertRCReturn(rc, rc);
1490 /* Clear the rest. */
1491 if (pState->nMacFilterEntries < VNET_MAC_FILTER_LEN)
1492 memset(&pState->aMacFilter[pState->nMacFilterEntries],
1493 0,
1494 (VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1495 * sizeof(RTMAC));
1496 rc = SSMR3GetMem(pSSM, pState->aVlanFilter,
1497 sizeof(pState->aVlanFilter));
1498 AssertRCReturn(rc, rc);
1499 }
1500 else
1501 {
1502 pState->fPromiscuous = true;
1503 pState->fAllMulti = false;
1504 pState->nMacFilterEntries = 0;
1505 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1506 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
1507 if (pState->pDrv)
1508 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv, true);
1509 }
1510 }
1511
1512 return rc;
1513}
1514
1515/**
1516 * Link status adjustments after loading.
1517 *
1518 * @returns VBox status code.
1519 * @param pDevIns The device instance.
1520 * @param pSSM The handle to the saved state.
1521 */
1522static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1523{
1524 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1525
1526 if (pState->pDrv)
1527 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1528 (pState->fPromiscuous | pState->fAllMulti));
1529 /*
1530 * Indicate link down to the guest OS that all network connections have
1531 * been lost, unless we've been teleported here.
1532 */
1533 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1534 vnetTempLinkDown(pState);
1535
1536 return VINF_SUCCESS;
1537}
1538
1539/**
1540 * Map PCI I/O region.
1541 *
1542 * @return VBox status code.
1543 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
1544 * @param iRegion The region number.
1545 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
1546 * I/O port, else it's a physical address.
1547 * This address is *NOT* relative to pci_mem_base like earlier!
1548 * @param cb Region size.
1549 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
1550 * @thread EMT
1551 */
1552static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
1553 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
1554{
1555 int rc;
1556 VNETSTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VNETSTATE*);
1557
1558 if (enmType != PCI_ADDRESS_SPACE_IO)
1559 {
1560 /* We should never get here */
1561 AssertMsgFailed(("Invalid PCI address space param in map callback"));
1562 return VERR_INTERNAL_ERROR;
1563 }
1564
1565 pState->VPCI.addrIOPort = (RTIOPORT)GCPhysAddress;
1566 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1567 cb, 0, vnetIOPortOut, vnetIOPortIn,
1568 NULL, NULL, "VirtioNet");
1569#ifdef VNET_GC_SUPPORT
1570 AssertRCReturn(rc, rc);
1571 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1572 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1573 NULL, NULL, "VirtioNet");
1574 AssertRCReturn(rc, rc);
1575 rc = PDMDevHlpIOPortRegisterRC(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1576 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1577 NULL, NULL, "VirtioNet");
1578#endif
1579 AssertRC(rc);
1580 return rc;
1581}
1582
1583/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
1584
1585#ifdef VBOX_DYNAMIC_NET_ATTACH
1586
1587/**
1588 * Detach notification.
1589 *
1590 * One port on the network card has been disconnected from the network.
1591 *
1592 * @param pDevIns The device instance.
1593 * @param iLUN The logical unit which is being detached.
1594 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1595 */
1596static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1597{
1598 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1599 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1600
1601 AssertLogRelReturnVoid(iLUN == 0);
1602
1603 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1604 if (RT_FAILURE(rc))
1605 {
1606 LogRel(("vnetDetach failed to enter critical section!\n"));
1607 return;
1608 }
1609
1610 /*
1611 * Zero some important members.
1612 */
1613 pState->pDrvBase = NULL;
1614 pState->pDrv = NULL;
1615
1616 vnetCsLeave(pState);
1617}
1618
1619
1620/**
1621 * Attach the Network attachment.
1622 *
1623 * One port on the network card has been connected to a network.
1624 *
1625 * @returns VBox status code.
1626 * @param pDevIns The device instance.
1627 * @param iLUN The logical unit which is being attached.
1628 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1629 *
1630 * @remarks This code path is not used during construction.
1631 */
1632static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1633{
1634 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1635 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1636
1637 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1638
1639 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1640 if (RT_FAILURE(rc))
1641 {
1642 LogRel(("vnetAttach failed to enter critical section!\n"));
1643 return rc;
1644 }
1645
1646 /*
1647 * Attach the driver.
1648 */
1649 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1650 if (RT_SUCCESS(rc))
1651 {
1652 if (rc == VINF_NAT_DNS)
1653 {
1654#ifdef RT_OS_LINUX
1655 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1656 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"));
1657#else
1658 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1659 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"));
1660#endif
1661 }
1662 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1663 AssertMsgStmt(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1664 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1665 }
1666 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1667 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
1668 {
1669 /* This should never happen because this function is not called
1670 * if there is no driver to attach! */
1671 Log(("%s No attached driver!\n", INSTANCE(pState)));
1672 }
1673
1674 /*
1675 * Temporary set the link down if it was up so that the guest
1676 * will know that we have change the configuration of the
1677 * network card
1678 */
1679 if (RT_SUCCESS(rc))
1680 vnetTempLinkDown(pState);
1681
1682 vnetCsLeave(pState);
1683 return rc;
1684
1685}
1686
1687#endif /* VBOX_DYNAMIC_NET_ATTACH */
1688
1689/**
1690 * @copydoc FNPDMDEVSUSPEND
1691 */
1692static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1693{
1694 /* Poke thread waiting for buffer space. */
1695 vnetWakeupReceive(pDevIns);
1696}
1697
1698/**
1699 * @copydoc FNPDMDEVPOWEROFF
1700 */
1701static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1702{
1703 /* Poke thread waiting for buffer space. */
1704 vnetWakeupReceive(pDevIns);
1705}
1706
1707/**
1708 * Device relocation callback.
1709 *
1710 * When this callback is called the device instance data, and if the
1711 * device have a GC component, is being relocated, or/and the selectors
1712 * have been changed. The device must use the chance to perform the
1713 * necessary pointer relocations and data updates.
1714 *
1715 * Before the GC code is executed the first time, this function will be
1716 * called with a 0 delta so GC pointer calculations can be one in one place.
1717 *
1718 * @param pDevIns Pointer to the device instance.
1719 * @param offDelta The relocation delta relative to the old location.
1720 *
1721 * @remark A relocation CANNOT fail.
1722 */
1723static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1724{
1725 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1726 vpciRelocate(pDevIns, offDelta);
1727 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1728#ifdef VNET_TX_DELAY
1729 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1730#endif /* VNET_TX_DELAY */
1731 // TBD
1732}
1733
1734/**
1735 * Destruct a device instance.
1736 *
1737 * We need to free non-VM resources only.
1738 *
1739 * @returns VBox status.
1740 * @param pDevIns The device instance data.
1741 * @thread EMT
1742 */
1743static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1744{
1745 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1746 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1747
1748 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
1749 pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1750 Log(("%s Destroying instance\n", INSTANCE(pState)));
1751 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1752 {
1753 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1754 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1755 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1756 }
1757
1758 // if (PDMCritSectIsInitialized(&pState->csRx))
1759 // PDMR3CritSectDelete(&pState->csRx);
1760
1761 return vpciDestruct(&pState->VPCI);
1762}
1763
1764/**
1765 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1766 */
1767static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1768{
1769 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1770 int rc;
1771 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1772
1773 /* Initialize PCI part first. */
1774 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1775 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance,
1776 VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
1777 VNET_PCI_CLASS, VNET_N_QUEUES);
1778 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive, "RX ");
1779 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit, "TX ");
1780 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl, "CTL");
1781
1782 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1783
1784 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1785
1786 /*
1787 * Validate configuration.
1788 */
1789 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "LineSpeed\0"))
1790 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1791 N_("Invalid configuration for VirtioNet device"));
1792
1793 /* Get config params */
1794 rc = CFGMR3QueryBytes(pCfg, "MAC", pState->macConfigured.au8,
1795 sizeof(pState->macConfigured));
1796 if (RT_FAILURE(rc))
1797 return PDMDEV_SET_ERROR(pDevIns, rc,
1798 N_("Configuration error: Failed to get MAC address"));
1799 rc = CFGMR3QueryBool(pCfg, "CableConnected", &pState->fCableConnected);
1800 if (RT_FAILURE(rc))
1801 return PDMDEV_SET_ERROR(pDevIns, rc,
1802 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1803
1804 /* Initialize PCI config space */
1805 memcpy(pState->config.mac.au8, pState->macConfigured.au8, sizeof(pState->config.mac.au8));
1806 pState->config.uStatus = 0;
1807
1808 /* Initialize state structure */
1809 pState->u32PktNo = 1;
1810
1811 /* Interfaces */
1812 pState->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
1813 pState->INetworkDown.pfnReceive = vnetNetworkDown_Receive;
1814 pState->INetworkDown.pfnXmitPending = vnetNetworkDown_XmitPending;
1815
1816 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1817 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1818 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1819
1820 /* Initialize critical section. */
1821 // char szTmp[sizeof(pState->VPCI.szInstance) + 2];
1822 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
1823 // rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, szTmp);
1824 // if (RT_FAILURE(rc))
1825 // return rc;
1826
1827 /* Map our ports to IO space. */
1828 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
1829 VPCI_CONFIG + sizeof(VNetPCIConfig),
1830 PCI_ADDRESS_SPACE_IO, vnetMap);
1831 if (RT_FAILURE(rc))
1832 return rc;
1833
1834
1835 /* Register save/restore state handlers. */
1836 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
1837 NULL, vnetLiveExec, NULL,
1838 vnetSavePrep, vnetSaveExec, NULL,
1839 vnetLoadPrep, vnetLoadExec, vnetLoadDone);
1840 if (RT_FAILURE(rc))
1841 return rc;
1842
1843 /* Create the RX notifier signaller. */
1844 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
1845 vnetCanRxQueueConsumer, true, "VNet-Rcv", &pState->pCanRxQueueR3);
1846 if (RT_FAILURE(rc))
1847 return rc;
1848 pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
1849 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1850
1851 /* Create Link Up Timer */
1852 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
1853 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1854 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
1855 if (RT_FAILURE(rc))
1856 return rc;
1857
1858#ifdef VNET_TX_DELAY
1859 /* Create Transmit Delay Timer */
1860 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pState,
1861 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1862 "VirtioNet TX Delay Timer", &pState->pTxTimerR3);
1863 if (RT_FAILURE(rc))
1864 return rc;
1865 pState->pTxTimerR0 = TMTimerR0Ptr(pState->pTxTimerR3);
1866 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1867
1868 pState->u32i = pState->u32AvgDiff = pState->u32MaxDiff = 0;
1869 pState->u32MinDiff = ~0;
1870#endif /* VNET_TX_DELAY */
1871
1872 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1873 if (RT_SUCCESS(rc))
1874 {
1875 if (rc == VINF_NAT_DNS)
1876 {
1877 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1878 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"));
1879 }
1880 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1881 AssertMsgReturn(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1882 VERR_PDM_MISSING_INTERFACE_BELOW);
1883 }
1884 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1885 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
1886 {
1887 /* No error! */
1888 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
1889 }
1890 else
1891 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
1892
1893 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
1894 if (RT_FAILURE(rc))
1895 return rc;
1896
1897 rc = vnetReset(pState);
1898 AssertRC(rc);
1899
1900 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/Bytes/Receive", iInstance);
1901 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/Bytes/Transmit", iInstance);
1902 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of received GSO packets", "/Devices/VNet%d/Packets/ReceiveGSO", iInstance);
1903 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitPackets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent packets", "/Devices/VNet%d/Packets/Transmit", iInstance);
1904 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent GSO packets", "/Devices/VNet%d/Packets/Transmit-Gso", iInstance);
1905 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitCSum, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of completed TX checksums", "/Devices/VNet%d/Packets/Transmit-Csum", iInstance);
1906#if defined(VBOX_WITH_STATISTICS)
1907 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
1908 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
1909 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
1910 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
1911 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
1912 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
1913#endif /* VBOX_WITH_STATISTICS */
1914
1915 return VINF_SUCCESS;
1916}
1917
1918/**
1919 * The device registration structure.
1920 */
1921const PDMDEVREG g_DeviceVirtioNet =
1922{
1923 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
1924 PDM_DEVREG_VERSION,
1925 /* Device name. */
1926 "virtio-net",
1927 /* Name of guest context module (no path).
1928 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1929 "VBoxDDGC.gc",
1930 /* Name of ring-0 module (no path).
1931 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1932 "VBoxDDR0.r0",
1933 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
1934 * remain unchanged from registration till VM destruction. */
1935 "Virtio Ethernet.\n",
1936
1937 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
1938#ifdef VNET_GC_SUPPORT
1939 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1940#else
1941 PDM_DEVREG_FLAGS_DEFAULT_BITS,
1942#endif
1943 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
1944 PDM_DEVREG_CLASS_NETWORK,
1945 /* Maximum number of instances (per VM). */
1946 8,
1947 /* Size of the instance data. */
1948 sizeof(VNETSTATE),
1949
1950 /* Construct instance - required. */
1951 vnetConstruct,
1952 /* Destruct instance - optional. */
1953 vnetDestruct,
1954 /* Relocation command - optional. */
1955 vnetRelocate,
1956 /* I/O Control interface - optional. */
1957 NULL,
1958 /* Power on notification - optional. */
1959 NULL,
1960 /* Reset notification - optional. */
1961 NULL,
1962 /* Suspend notification - optional. */
1963 vnetSuspend,
1964 /* Resume notification - optional. */
1965 NULL,
1966#ifdef VBOX_DYNAMIC_NET_ATTACH
1967 /* Attach command - optional. */
1968 vnetAttach,
1969 /* Detach notification - optional. */
1970 vnetDetach,
1971#else /* !VBOX_DYNAMIC_NET_ATTACH */
1972 /* Attach command - optional. */
1973 NULL,
1974 /* Detach notification - optional. */
1975 NULL,
1976#endif /* !VBOX_DYNAMIC_NET_ATTACH */
1977 /* Query a LUN base interface - optional. */
1978 NULL,
1979 /* Init complete notification - optional. */
1980 NULL,
1981 /* Power off notification - optional. */
1982 vnetPowerOff,
1983 /* pfnSoftReset */
1984 NULL,
1985 /* u32VersionEnd */
1986 PDM_DEVREG_VERSION
1987};
1988
1989#endif /* IN_RING3 */
1990#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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