VirtualBox

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

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

#3987: Virtio PCI + Net skeleton.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.6 KB
Line 
1/* $Id: DevVirtioNet.cpp 22866 2009-09-09 14:39:46Z vboxsync $ */
2/** @file
3 * DevVirtioNet - Virtio Network Device
4 *
5 */
6
7/*
8 * Copyright (C) 2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
25
26#include <iprt/ctype.h>
27#ifdef IN_RING3
28# include <iprt/mem.h>
29#endif /* IN_RING3 */
30#include <iprt/param.h>
31#include <iprt/semaphore.h>
32#include <VBox/pdmdev.h>
33#include <VBox/tm.h>
34#include "../Builtins.h"
35#if 0
36#include <iprt/crc32.h>
37#include <iprt/string.h>
38#include <VBox/vm.h>
39#endif
40
41// TODO: move declarations to the header file: #include "DevVirtioNet.h"
42
43#define INSTANCE(pState) pState->szInstance
44#define IFACE_TO_STATE(pIface, ifaceName) ((VPCISTATE *)((char*)pIface - RT_OFFSETOF(VPCISTATE, ifaceName)))
45#if 0
46/* Little helpers ************************************************************/
47#undef htons
48#undef ntohs
49#undef htonl
50#undef ntohl
51#define htons(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))
52#define ntohs(x) htons(x)
53#define htonl(x) ASMByteSwapU32(x)
54#define ntohl(x) htonl(x)
55
56#define E1K_RELOCATE(p, o) *(RTHCUINTPTR *)&p += o
57
58#define E1K_INC_CNT32(cnt) \
59do { \
60 if (cnt < UINT32_MAX) \
61 cnt++; \
62} while (0)
63
64#define E1K_ADD_CNT64(cntLo, cntHi, val) \
65do { \
66 uint64_t u64Cnt = RT_MAKE_U64(cntLo, cntHi); \
67 uint64_t tmp = u64Cnt; \
68 u64Cnt += val; \
69 if (tmp > u64Cnt ) \
70 u64Cnt = UINT64_MAX; \
71 cntLo = (uint32_t)u64Cnt; \
72 cntHi = (uint32_t)(u64Cnt >> 32); \
73} while (0)
74
75#ifdef E1K_INT_STATS
76# define E1K_INC_ISTAT_CNT(cnt) ++cnt
77#else /* E1K_INT_STATS */
78# define E1K_INC_ISTAT_CNT(cnt)
79#endif /* E1K_INT_STATS */
80#endif
81
82/*****************************************************************************/
83RT_C_DECLS_BEGIN
84PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pState);
85PDMBOTHCBDECL(int) vnetGetConfig(void *pState, uint32_t port, uint32_t cb, void *data);
86PDMBOTHCBDECL(int) vnetSetConfig(void *pState, uint32_t port, uint32_t cb, void *data);
87PDMBOTHCBDECL(void) vnetReset(void *pState);
88RT_C_DECLS_END
89
90/*****************************************************************************/
91
92struct VRingDesc
93{
94 uint64_t u64Addr;
95 uint32_t uLen;
96 uint16_t u16Flags;
97 uint16_t u16Next;
98};
99typedef struct VRingDesc VRINGDESC;
100
101struct VRing
102{
103 uint16_t uSize;
104 VRINGDESC *pDescriptors;
105};
106typedef struct VRing VRING;
107
108struct VQueue
109{
110 VRING VRing;
111 uint16_t uPageNumber;
112 void (*pfnCallback)(void *pvState, struct VQueue *pQueue);
113};
114typedef struct VQueue VQUEUE;
115typedef VQUEUE *PVQUEUE;
116
117enum VirtioDeviceType
118{
119 VIRTIO_NET_ID = 0,
120 VIRTIO_BLK_ID = 1
121};
122
123struct VPCIState_st
124{
125 /* Read-only part, never changes after initialization. */
126 VirtioDeviceType enmDevType; /**< Device type: net or blk. */
127 char szInstance[8]; /**< Instance name, e.g. VNet#1. */
128
129 PDMIBASE IBase;
130 PDMILEDPORTS ILeds; /**< LED interface */
131 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
132
133 PPDMDEVINSR3 pDevInsR3; /**< Device instance - R3. */
134 PPDMDEVINSR0 pDevInsR0; /**< Device instance - R0. */
135 PPDMDEVINSRC pDevInsRC; /**< Device instance - RC. */
136
137 /** TODO */
138 PCIDEVICE pciDevice;
139 /** Base port of I/O space region. */
140 RTIOPORT addrIOPort;
141
142 /* Read/write part, protected with critical section. */
143 PDMCRITSECT cs; /**< Critical section - what is it protecting? */
144 /** Status LED. */
145 PDMLED led;
146
147 uint32_t uGuestFeatures;
148 uint16_t uQueueSelector; /**< An index in aQueues array. */
149 uint8_t uStatus; /**< Device Status (bits are device-specific). */
150 uint8_t uISR; /**< Interrupt Status Register. */
151 PVQUEUE pQueues;
152
153#if defined(VBOX_WITH_STATISTICS)
154 STAMPROFILEADV StatIOReadGC;
155 STAMPROFILEADV StatIOReadHC;
156 STAMPROFILEADV StatIOWriteGC;
157 STAMPROFILEADV StatIOWriteHC;
158 STAMCOUNTER StatIntsRaised;
159#endif /* VBOX_WITH_STATISTICS */
160};
161typedef struct VPCIState_st VPCISTATE;
162
163struct VirtioPCIDevices
164{
165 uint16_t uPCIVendorId;
166 uint16_t uPCIDeviceId;
167 uint16_t uPCISubsystemVendorId;
168 uint16_t uPCISubsystemId;
169 uint16_t uPCIClass;
170 unsigned nQueues;
171 const char *pcszName;
172 const char *pcszNameFmt;
173 uint32_t (*pfnGetHostFeatures)(void *pvState);
174 int (*pfnGetConfig)(void *pvState, uint32_t port, uint32_t cb, void *data);
175 int (*pfnSetConfig)(void *pvState, uint32_t port, uint32_t cb, void *data);
176 void (*pfnReset)(void *pvState);
177} g_VPCIDevices[] =
178{
179 /* Vendor Device SSVendor SubSys Class Name */
180 { 0x1AF4, 0x1000, 0x1AF4, 1 + VIRTIO_NET_ID, 0x0200, 3, "virtio-net", "vnet%d",
181 vnetGetHostFeatures, vnetGetConfig, vnetSetConfig, vnetReset }, /* Virtio Network Device */
182 { 0x1AF4, 0x1001, 0x1AF4, 1 + VIRTIO_BLK_ID, 0x0180, 2, "virtio-blk", "vblk%d",
183 NULL, NULL, NULL, NULL }, /* Virtio Block Device */
184};
185
186
187/*****************************************************************************/
188
189#define VPCI_HOST_FEATURES 0x0
190#define VPCI_GUEST_FEATURES 0x4
191#define VPCI_QUEUE_PFN 0x8
192#define VPCI_QUEUE_NUM 0xC
193#define VPCI_QUEUE_SEL 0xE
194#define VPCI_QUEUE_NOTIFY 0x10
195#define VPCI_STATUS 0x12
196#define VPCI_ISR 0x13
197#define VPCI_ISR_QUEUE 0x1
198#define VPCI_ISR_CONFIG 0x3
199#define VPCI_CONFIG 0x14
200
201/** @todo use+extend RTNETIPV4 */
202
203/** @todo use+extend RTNETTCP */
204
205#define VNET_SAVEDSTATE_VERSION 1
206
207#ifndef VBOX_DEVICE_STRUCT_TESTCASE
208
209/* Forward declarations ******************************************************/
210RT_C_DECLS_BEGIN
211PDMBOTHCBDECL(int) vpciIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
212PDMBOTHCBDECL(int) vpciIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
213RT_C_DECLS_END
214
215/**
216 * Arm a timer.
217 *
218 * @param pState Pointer to the device state structure.
219 * @param pTimer Pointer to the timer.
220 * @param uExpireIn Expiration interval in microseconds.
221 */
222DECLINLINE(void) vpciArmTimer(VPCISTATE *pState, PTMTIMER pTimer, uint32_t uExpireIn)
223{
224 Log2(("%s Arming timer to fire in %d usec...\n",
225 INSTANCE(pState), uExpireIn));
226 TMTimerSet(pTimer, TMTimerFromMicro(pTimer, uExpireIn) +
227 TMTimerGet(pTimer));
228}
229
230
231DECLINLINE(int) vpciCsEnter(VPCISTATE *pState, int iBusyRc)
232{
233 return PDMCritSectEnter(&pState->cs, iBusyRc);
234}
235
236DECLINLINE(void) vpciCsLeave(VPCISTATE *pState)
237{
238 PDMCritSectLeave(&pState->cs);
239}
240
241/**
242 * Raise interrupt.
243 *
244 * @param pState The device state structure.
245 * @param rcBusy Status code to return when the critical section is busy.
246 * @param u8IntCause Interrupt cause bit mask to set in PCI ISR port.
247 */
248PDMBOTHCBDECL(int) vpciRaiseInterrupt(VPCISTATE *pState, int rcBusy, uint8_t u8IntCause)
249{
250 int rc = vpciCsEnter(pState, rcBusy);
251 if (RT_UNLIKELY(rc != VINF_SUCCESS))
252 return rc;
253
254 pState->uISR |= u8IntCause;
255 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 1);
256 vpciCsLeave(pState);
257 return VINF_SUCCESS;
258}
259
260/**
261 * Lower interrupt.
262 *
263 * @param pState The device state structure.
264 */
265PDMBOTHCBDECL(void) vpciLowerInterrupt(VPCISTATE *pState)
266{
267 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 0);
268}
269
270/**
271 * Port I/O Handler for IN operations.
272 *
273 * @returns VBox status code.
274 *
275 * @param pDevIns The device instance.
276 * @param pvUser Pointer to the device state structure.
277 * @param port Port number used for the IN operation.
278 * @param pu32 Where to store the result.
279 * @param cb Number of bytes read.
280 * @thread EMT
281 */
282PDMBOTHCBDECL(int) vpciIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
283 RTIOPORT port, uint32_t *pu32, unsigned cb)
284{
285 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
286 int rc = VINF_SUCCESS;
287 const char *szInst = INSTANCE(pState);
288 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIORead), a);
289
290 port -= pState->addrIOPort;
291 switch (port)
292 {
293 case VPCI_HOST_FEATURES:
294 /* Tell the guest what features we support. */
295 *pu32 = g_VPCIDevices[pState->enmDevType].pfnGetHostFeatures(pState);
296 break;
297
298 case VPCI_GUEST_FEATURES:
299 *pu32 = pState->uGuestFeatures;
300 break;
301
302 case VPCI_QUEUE_PFN:
303 *pu32 = pState->pQueues[pState->uQueueSelector].uPageNumber;
304 break;
305
306 case VPCI_QUEUE_NUM:
307 Assert(cb == 2);
308 *(uint16_t*)pu32 = pState->pQueues[pState->uQueueSelector].VRing.uSize;
309 break;
310
311 case VPCI_QUEUE_SEL:
312 Assert(cb == 2);
313 *(uint16_t*)pu32 = pState->uQueueSelector;
314 break;
315
316 case VPCI_STATUS:
317 Assert(cb == 1);
318 *(uint8_t*)pu32 = pState->uStatus;
319 break;
320
321 case VPCI_ISR:
322 Assert(cb == 1);
323 *(uint8_t*)pu32 = pState->uISR;
324 pState->uISR = 0; /* read clears all interrupts */
325 break;
326
327 default:
328 if (port >= VPCI_CONFIG)
329 rc = g_VPCIDevices[pState->enmDevType].pfnGetConfig(pState, port - VPCI_CONFIG, cb, pu32);
330 else
331 {
332 *pu32 = 0xFFFFFFFF;
333 rc = PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "%s virtioIOPortIn: no valid port at offset port=%RTiop cb=%08x\n", szInst, port, cb);
334 }
335 break;
336 }
337 Log3(("%s virtioIOPortIn: At %RTiop in %0*x\n", szInst, port, cb*2, *pu32));
338 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIORead), a);
339 return rc;
340}
341
342
343/**
344 * Port I/O Handler for OUT operations.
345 *
346 * @returns VBox status code.
347 *
348 * @param pDevIns The device instance.
349 * @param pvUser User argument.
350 * @param Port Port number used for the IN operation.
351 * @param u32 The value to output.
352 * @param cb The value size in bytes.
353 * @thread EMT
354 */
355PDMBOTHCBDECL(int) vpciIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
356 RTIOPORT port, uint32_t u32, unsigned cb)
357{
358 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
359 int rc = VINF_SUCCESS;
360 const char *szInst = INSTANCE(pState);
361 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIOWrite), a);
362
363 port -= pState->addrIOPort;
364 Log3(("%s virtioIOPortOut: At %RTiop out %0*x\n", szInst, port, cb*2, u32));
365
366 switch (port)
367 {
368 case VPCI_GUEST_FEATURES:
369 // TODO: Feature negotiation code goes here.
370 // The guest may potentially desire features we don't support!
371 break;
372
373 case VPCI_QUEUE_PFN:
374 /*
375 * The guest is responsible for allocating the pages for queues,
376 * here it provides us with the page number of descriptor table.
377 * Note that we provide the size of the queue to the guest via
378 * VIRTIO_PCI_QUEUE_NUM.
379 */
380 pState->pQueues[pState->uQueueSelector].uPageNumber = u32;
381 pState->pQueues[pState->uQueueSelector].VRing.pDescriptors = (VRINGDESC*)(u32 << PAGE_SHIFT);
382 break;
383
384 case VPCI_QUEUE_SEL:
385 Assert(cb == 2);
386 u32 &= 0xFFFF;
387 if (u32 < g_VPCIDevices[pState->enmDevType].nQueues)
388 pState->uQueueSelector = u32;
389 else
390 Log3(("%s virtioIOPortOut: Invalid queue selector %08x\n", szInst, u32));
391 break;
392
393 case VPCI_QUEUE_NOTIFY:
394 // TODO
395 break;
396
397 case VPCI_STATUS:
398 Assert(cb == 1);
399 u32 &= 0xFF;
400 pState->uStatus = u32;
401 /* Writing 0 to the status port triggers device reset. */
402 if (u32 == 0)
403 g_VPCIDevices[pState->enmDevType].pfnReset(pState);
404 break;
405
406 default:
407 if (port >= VPCI_CONFIG)
408 rc = g_VPCIDevices[pState->enmDevType].pfnSetConfig(pState, port - VPCI_CONFIG, cb, &u32);
409 else
410 rc = PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "%s virtioIOPortOut: no valid port at offset port=%RTiop cb=%08x\n", szInst, port, cb);
411 break;
412 }
413
414 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIOWrite), a);
415 return rc;
416}
417
418#ifdef IN_RING3
419// Common
420/**
421 * Map PCI I/O region.
422 *
423 * @return VBox status code.
424 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
425 * @param iRegion The region number.
426 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
427 * I/O port, else it's a physical address.
428 * This address is *NOT* relative to pci_mem_base like earlier!
429 * @param cb Region size.
430 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
431 * @thread EMT
432 */
433static DECLCALLBACK(int) vpciMap(PPCIDEVICE pPciDev, int iRegion,
434 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
435{
436 int rc;
437 VPCISTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VPCISTATE*);
438
439 if (enmType != PCI_ADDRESS_SPACE_IO)
440 {
441 /* We should never get here */
442 AssertMsgFailed(("Invalid PCI address space param in map callback"));
443 return VERR_INTERNAL_ERROR;
444 }
445
446 pState->addrIOPort = (RTIOPORT)GCPhysAddress;
447 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
448 vpciIOPortOut, vpciIOPortIn, NULL, NULL, "VirtioNet");
449#if 0
450 AssertRCReturn(rc, rc);
451 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
452 "vpciIOPortOut", "vpciIOPortIn", NULL, NULL, "VirtioNet");
453 AssertRCReturn(rc, rc);
454 rc = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
455 "vpciIOPortOut", "vpciIOPortIn", NULL, NULL, "VirtioNet");
456#endif
457 AssertRC(rc);
458 return rc;
459}
460
461/**
462 * Provides interfaces to the driver.
463 *
464 * @returns Pointer to interface. NULL if the interface is not supported.
465 * @param pInterface Pointer to this interface structure.
466 * @param enmInterface The requested interface identification.
467 * @thread EMT
468 */
469static DECLCALLBACK(void *) vpciQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
470{
471 VPCISTATE *pState = IFACE_TO_STATE(pInterface, IBase);
472 Assert(&pState->IBase == pInterface);
473 switch (enmInterface)
474 {
475 case PDMINTERFACE_BASE:
476 return &pState->IBase;
477 case PDMINTERFACE_LED_PORTS:
478 return &pState->ILeds;
479 default:
480 return NULL;
481 }
482}
483
484/**
485 * Gets the pointer to the status LED of a unit.
486 *
487 * @returns VBox status code.
488 * @param pInterface Pointer to the interface structure.
489 * @param iLUN The unit which status LED we desire.
490 * @param ppLed Where to store the LED pointer.
491 * @thread EMT
492 */
493static DECLCALLBACK(int) vpciQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
494{
495 VPCISTATE *pState = IFACE_TO_STATE(pInterface, ILeds);
496 int rc = VERR_PDM_LUN_NOT_FOUND;
497
498 if (iLUN == 0)
499 {
500 *ppLed = &pState->led;
501 rc = VINF_SUCCESS;
502 }
503 return rc;
504}
505
506/**
507 * Sets 8-bit register in PCI configuration space.
508 * @param refPciDev The PCI device.
509 * @param uOffset The register offset.
510 * @param u16Value The value to store in the register.
511 * @thread EMT
512 */
513DECLINLINE(void) virtioPCICfgSetU8(PCIDEVICE& refPciDev, uint32_t uOffset, uint8_t u8Value)
514{
515 Assert(uOffset < sizeof(refPciDev.config));
516 refPciDev.config[uOffset] = u8Value;
517}
518
519/**
520 * Sets 16-bit register in PCI configuration space.
521 * @param refPciDev The PCI device.
522 * @param uOffset The register offset.
523 * @param u16Value The value to store in the register.
524 * @thread EMT
525 */
526DECLINLINE(void) virtioPCICfgSetU16(PCIDEVICE& refPciDev, uint32_t uOffset, uint16_t u16Value)
527{
528 Assert(uOffset+sizeof(u16Value) <= sizeof(refPciDev.config));
529 *(uint16_t*)&refPciDev.config[uOffset] = u16Value;
530}
531
532/**
533 * Sets 32-bit register in PCI configuration space.
534 * @param refPciDev The PCI device.
535 * @param uOffset The register offset.
536 * @param u32Value The value to store in the register.
537 * @thread EMT
538 */
539DECLINLINE(void) virtioPCICfgSetU32(PCIDEVICE& refPciDev, uint32_t uOffset, uint32_t u32Value)
540{
541 Assert(uOffset+sizeof(u32Value) <= sizeof(refPciDev.config));
542 *(uint32_t*)&refPciDev.config[uOffset] = u32Value;
543}
544
545
546/**
547 * Set PCI configuration space registers.
548 *
549 * @param pci Reference to PCI device structure.
550 * @thread EMT
551 */
552static DECLCALLBACK(void) vpciConfigure(PCIDEVICE& pci, VirtioDeviceType enmType)
553{
554 Assert(enmType < (int)RT_ELEMENTS(g_VPCIDevices));
555 /* Configure PCI Device, assume 32-bit mode ******************************/
556 PCIDevSetVendorId(&pci, g_VPCIDevices[enmType].uPCIVendorId);
557 PCIDevSetDeviceId(&pci, g_VPCIDevices[enmType].uPCIDeviceId);
558 virtioPCICfgSetU16(pci, VBOX_PCI_SUBSYSTEM_VENDOR_ID, g_VPCIDevices[enmType].uPCISubsystemVendorId);
559 virtioPCICfgSetU16(pci, VBOX_PCI_SUBSYSTEM_ID, g_VPCIDevices[enmType].uPCISubsystemId);
560
561 /* ABI version, must be equal 0 as of 2.6.30 kernel. */
562 virtioPCICfgSetU8( pci, VBOX_PCI_REVISION_ID, 0x00);
563 /* Ethernet adapter */
564 virtioPCICfgSetU8( pci, VBOX_PCI_CLASS_PROG, 0x00);
565 virtioPCICfgSetU16(pci, VBOX_PCI_CLASS_DEVICE, g_VPCIDevices[enmType].uPCIClass);
566 /* Interrupt Pin: INTA# */
567 virtioPCICfgSetU8( pci, VBOX_PCI_INTERRUPT_PIN, 0x01);
568}
569
570// TODO: header
571DECLCALLBACK(int) vpciConstruct(PPDMDEVINS pDevIns, VPCISTATE *pState,
572 int iInstance, VirtioDeviceType enmDevType,
573 unsigned uConfigSize)
574{
575 int rc = VINF_SUCCESS;
576 /* Init handles and log related stuff. */
577 RTStrPrintf(pState->szInstance, sizeof(pState->szInstance), g_VPCIDevices[enmDevType].pcszNameFmt, iInstance);
578 pState->enmDevType = enmDevType;
579
580 /* Allocate queues */
581 pState->pQueues = (VQUEUE*)RTMemAllocZ(sizeof(VQUEUE) * g_VPCIDevices[enmDevType].nQueues);
582
583 pState->pDevInsR3 = pDevIns;
584 pState->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
585 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
586 pState->led.u32Magic = PDMLED_MAGIC;
587
588 pState->ILeds.pfnQueryStatusLed = vpciQueryStatusLed;
589
590 /* Initialize critical section. */
591 rc = PDMDevHlpCritSectInit(pDevIns, &pState->cs, pState->szInstance);
592 if (RT_FAILURE(rc))
593 return rc;
594
595 /* Set PCI config registers */
596 vpciConfigure(pState->pciDevice, VIRTIO_NET_ID);
597 /* Register PCI device */
598 rc = PDMDevHlpPCIRegister(pDevIns, &pState->pciDevice);
599 if (RT_FAILURE(rc))
600 return rc;
601
602 /* Map our ports to IO space. */
603 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, VPCI_CONFIG + uConfigSize,
604 PCI_ADDRESS_SPACE_IO, vpciMap);
605 if (RT_FAILURE(rc))
606 return rc;
607
608 /* Status driver */
609 PPDMIBASE pBase;
610 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pState->IBase, &pBase, "Status Port");
611 if (RT_FAILURE(rc))
612 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
613 pState->pLedsConnector = (PPDMILEDCONNECTORS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_LED_CONNECTORS);
614
615#if defined(VBOX_WITH_STATISTICS)
616 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in GC", "/Devices/VNet%d/IO/ReadGC", iInstance);
617 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in HC", "/Devices/VNet%d/IO/ReadHC", iInstance);
618 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in GC", "/Devices/VNet%d/IO/WriteGC", iInstance);
619 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in HC", "/Devices/VNet%d/IO/WriteHC", iInstance);
620 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIntsRaised, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of raised interrupts", "/Devices/VNet%d/Interrupts/Raised", iInstance);
621#endif /* VBOX_WITH_STATISTICS */
622
623 return rc;
624}
625
626/**
627 * Destruct PCI-related part of device.
628 *
629 * We need to free non-VM resources only.
630 *
631 * @returns VBox status.
632 * @param pState The device state structure.
633 */
634static DECLCALLBACK(int) vpciDestruct(VPCISTATE* pState)
635{
636 Log(("%s Destroying PCI instance\n", INSTANCE(pState)));
637
638 if (pState->pQueues)
639 RTMemFree(pState->pQueues);
640
641 if (PDMCritSectIsInitialized(&pState->cs))
642 {
643 PDMR3CritSectDelete(&pState->cs);
644 }
645 return VINF_SUCCESS;
646}
647
648/**
649 * Device relocation callback.
650 *
651 * When this callback is called the device instance data, and if the
652 * device have a GC component, is being relocated, or/and the selectors
653 * have been changed. The device must use the chance to perform the
654 * necessary pointer relocations and data updates.
655 *
656 * Before the GC code is executed the first time, this function will be
657 * called with a 0 delta so GC pointer calculations can be one in one place.
658 *
659 * @param pDevIns Pointer to the device instance.
660 * @param offDelta The relocation delta relative to the old location.
661 *
662 * @remark A relocation CANNOT fail.
663 */
664static DECLCALLBACK(void) vpciRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
665{
666 VPCISTATE* pState = PDMINS_2_DATA(pDevIns, VPCISTATE*);
667 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
668 // TBD
669}
670
671PVQUEUE vpciAddQueue(VPCISTATE* pState, unsigned uSize,
672 void (*pfnCallback)(void *pvState, PVQUEUE pQueue))
673{
674 PVQUEUE pQueue = NULL;
675 /* Find an empty queue slot */
676 for (unsigned i = 0; i < g_VPCIDevices[pState->enmDevType].nQueues; i++)
677 {
678 if (pState->pQueues[i].VRing.uSize == 0)
679 {
680 pQueue = &pState->pQueues[i];
681 break;
682 }
683 }
684
685 if (!pQueue)
686 {
687 Log(("%s Too many queues being added, no empty slots available!\n", INSTANCE(pState)));
688 }
689 else
690 {
691 pQueue->VRing.uSize = uSize;
692 pQueue->VRing.pDescriptors = NULL;
693 pQueue->uPageNumber = 0;
694 pQueue->pfnCallback = pfnCallback;
695 }
696
697 return pQueue;
698}
699
700#endif /* IN_RING3 */
701
702
703//------------------------- Tear off here: vnet -------------------------------
704
705/* Virtio net features */
706#define NET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
707#define NET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
708#define NET_F_MAC 0x00000020 /* Host has given MAC address. */
709#define NET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
710#define NET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
711#define NET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
712#define NET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
713#define NET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
714#define NET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
715#define NET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
716#define NET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
717#define NET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
718#define NET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
719#define NET_F_STATUS 0x00010000 /* virtio_net_config.status available */
720#define NET_F_CTRL_VQ 0x00020000 /* Control channel available */
721#define NET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
722#define NET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
723
724#define NET_S_LINK_UP 1
725
726
727#ifdef _MSC_VER
728struct VNetPCIConfig
729#else /* !_MSC_VER */
730struct __attribute__ ((__packed__)) VNetPCIConfig
731#endif /* !_MSC_VER */
732{
733 RTMAC mac;
734 uint16_t uStatus;
735};
736
737AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
738
739/**
740 * Device state structure. Holds the current state of device.
741 */
742
743struct VNetState_st
744{
745 /* VPCISTATE must be the first member! */
746 VPCISTATE VPCI;
747
748 PDMINETWORKPORT INetworkPort;
749 PDMINETWORKCONFIG INetworkConfig;
750 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
751 R3PTRTYPE(PPDMINETWORKCONNECTOR) pDrv; /**< Connector of attached network driver. */
752
753 PTMTIMERR3 pLinkUpTimer; /**< Link Up(/Restore) Timer. */
754
755 /** PCI config area holding MAC address as well as TBD. */
756 struct VNetPCIConfig config;
757 /** True if physical cable is attached in configuration. */
758 bool fCableConnected;
759
760 /** Number of packet being sent/received to show in debug log. */
761 uint32_t u32PktNo;
762
763 /** Locked state -- no state alteration possible. */
764 bool fLocked;
765
766 PVQUEUE pRxQueue;
767 PVQUEUE pTxQueue;
768 PVQUEUE pCtlQueue;
769 /* Receive-blocking-related fields ***************************************/
770
771 /** N/A: */
772 bool volatile fMaybeOutOfSpace;
773 /** EMT: Gets signalled when more RX descriptors become available. */
774 RTSEMEVENT hEventMoreRxDescAvail;
775
776 R3PTRTYPE(PPDMQUEUE) pCanRxQueueR3; /**< Rx wakeup signaller - R3. */
777 R0PTRTYPE(PPDMQUEUE) pCanRxQueueR0; /**< Rx wakeup signaller - R0. */
778 RCPTRTYPE(PPDMQUEUE) pCanRxQueueRC; /**< Rx wakeup signaller - RC. */
779
780 /* Statistic fields ******************************************************/
781
782 STAMCOUNTER StatReceiveBytes;
783 STAMCOUNTER StatTransmitBytes;
784#if defined(VBOX_WITH_STATISTICS)
785 STAMPROFILEADV StatReceive;
786 STAMPROFILEADV StatTransmit;
787 STAMPROFILEADV StatTransmitSend;
788 STAMPROFILE StatRxOverflow;
789 STAMCOUNTER StatRxOverflowWakeup;
790#endif /* VBOX_WITH_STATISTICS */
791
792};
793typedef struct VNetState_st VNETSTATE;
794
795AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
796
797#undef INSTANCE
798#define INSTANCE(pState) pState->VPCI.szInstance
799#undef IFACE_TO_STATE
800#define IFACE_TO_STATE(pIface, ifaceName) ((VNETSTATE *)((char*)pIface - RT_OFFSETOF(VNETSTATE, ifaceName)))
801#define STATUS pState->config.uStatus
802
803PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
804{
805 // TODO: implement
806 return NET_F_MAC | NET_F_STATUS;
807}
808
809PDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
810{
811 VNETSTATE *pState = (VNETSTATE *)pvState;
812 if (port + cb > sizeof(struct VNetPCIConfig))
813 {
814 Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
815 return VERR_INTERNAL_ERROR;
816 }
817 memcpy(data, ((uint8_t*)&pState->config) + port, cb);
818 return VINF_SUCCESS;
819}
820
821PDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
822{
823 VNETSTATE *pState = (VNETSTATE *)pvState;
824 if (port + cb > sizeof(struct VNetPCIConfig))
825 {
826 Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
827 return VERR_INTERNAL_ERROR;
828 }
829 memcpy(((uint8_t*)&pState->config) + port, data, cb);
830 return VINF_SUCCESS;
831}
832
833/**
834 * Hardware reset. Revert all registers to initial values.
835 *
836 * @param pState The device state structure.
837 */
838PDMBOTHCBDECL(void) vnetReset(void *pvState)
839{
840 VNETSTATE *pState = (VNETSTATE*)pvState;
841 Log(("%s Reset triggered\n", INSTANCE(pState)));
842 // TODO: Implement reset
843}
844
845#ifdef IN_RING3
846/**
847 * Wakeup the RX thread.
848 */
849static void vnetWakeupReceive(PPDMDEVINS pDevIns)
850{
851 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
852 if ( pState->fMaybeOutOfSpace
853 && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
854 {
855 STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
856 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
857 RTSemEventSignal(pState->hEventMoreRxDescAvail);
858 }
859}
860
861/**
862 * Link Up Timer handler.
863 *
864 * @param pDevIns Pointer to device instance structure.
865 * @param pTimer Pointer to the timer.
866 * @param pvUser NULL.
867 * @thread EMT
868 */
869static DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
870{
871 VNETSTATE *pState = (VNETSTATE *)pvUser;
872
873 STATUS |= NET_S_LINK_UP;
874 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
875}
876
877
878
879
880/**
881 * Handler for the wakeup signaller queue.
882 */
883static DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
884{
885 vnetWakeupReceive(pDevIns);
886 return true;
887}
888
889#endif /* IN_RING3 */
890
891
892#ifdef IN_RING3
893
894/**
895 * Check if the device can receive data now.
896 * This must be called before the pfnRecieve() method is called.
897 *
898 * @returns Number of bytes the device can receive.
899 * @param pInterface Pointer to the interface structure containing the called function pointer.
900 * @thread EMT
901 */
902static int vnetCanReceive(VNETSTATE *pState)
903{
904 size_t cb;
905
906 cb = 0; // TODO
907 return cb > 0 ? VINF_SUCCESS : VERR_NET_NO_BUFFER_SPACE;
908}
909
910static DECLCALLBACK(int) vnetWaitReceiveAvail(PPDMINETWORKPORT pInterface, unsigned cMillies)
911{
912 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
913 int rc = vnetCanReceive(pState);
914
915 if (RT_SUCCESS(rc))
916 return VINF_SUCCESS;
917 if (RT_UNLIKELY(cMillies == 0))
918 return VERR_NET_NO_BUFFER_SPACE;
919
920 rc = VERR_INTERRUPTED;
921 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
922 STAM_PROFILE_START(&pState->StatRxOverflow, a);
923 while (RT_LIKELY(PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns)) == VMSTATE_RUNNING))
924 {
925 int rc2 = vnetCanReceive(pState);
926 if (RT_SUCCESS(rc2))
927 {
928 rc = VINF_SUCCESS;
929 break;
930 }
931 Log(("%s vnetWaitReceiveAvail: waiting cMillies=%u...\n",
932 INSTANCE(pState), cMillies));
933 RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
934 }
935 STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
936 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
937
938 return rc;
939}
940
941
942/**
943 * Provides interfaces to the driver.
944 *
945 * @returns Pointer to interface. NULL if the interface is not supported.
946 * @param pInterface Pointer to this interface structure.
947 * @param enmInterface The requested interface identification.
948 * @thread EMT
949 */
950static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
951{
952 VNETSTATE *pState = IFACE_TO_STATE(pInterface, VPCI.IBase);
953 Assert(&pState->VPCI.IBase == pInterface);
954 switch (enmInterface)
955 {
956 case PDMINTERFACE_NETWORK_PORT:
957 return &pState->INetworkPort;
958 case PDMINTERFACE_NETWORK_CONFIG:
959 return &pState->INetworkConfig;
960 default:
961 return vpciQueryInterface(pInterface, enmInterface);
962 }
963}
964
965/**
966 * Receive data from the network.
967 *
968 * @returns VBox status code.
969 * @param pInterface Pointer to the interface structure containing the called function pointer.
970 * @param pvBuf The available data.
971 * @param cb Number of bytes available in the buffer.
972 * @thread ???
973 */
974static DECLCALLBACK(int) vnetReceive(PPDMINETWORKPORT pInterface, const void *pvBuf, size_t cb)
975{
976 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
977 int rc = VINF_SUCCESS;
978 // TODO: Implement
979 return rc;
980}
981
982/**
983 * Gets the current Media Access Control (MAC) address.
984 *
985 * @returns VBox status code.
986 * @param pInterface Pointer to the interface structure containing the called function pointer.
987 * @param pMac Where to store the MAC address.
988 * @thread EMT
989 */
990static DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
991{
992 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
993 memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
994 return VINF_SUCCESS;
995}
996
997/**
998 * Gets the new link state.
999 *
1000 * @returns The current link state.
1001 * @param pInterface Pointer to the interface structure containing the called function pointer.
1002 * @thread EMT
1003 */
1004static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
1005{
1006 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1007 if (STATUS & NET_S_LINK_UP)
1008 return PDMNETWORKLINKSTATE_UP;
1009 return PDMNETWORKLINKSTATE_DOWN;
1010}
1011
1012
1013/**
1014 * Sets the new link state.
1015 *
1016 * @returns VBox status code.
1017 * @param pInterface Pointer to the interface structure containing the called function pointer.
1018 * @param enmState The new link state
1019 */
1020static DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
1021{
1022 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1023 bool fOldUp = !!(STATUS & NET_S_LINK_UP);
1024 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
1025
1026 if (fNewUp != fOldUp)
1027 {
1028 if (fNewUp)
1029 {
1030 Log(("%s Link is up\n", INSTANCE(pState)));
1031 STATUS |= NET_S_LINK_UP;
1032 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1033 }
1034 else
1035 {
1036 Log(("%s Link is down\n", INSTANCE(pState)));
1037 STATUS &= ~NET_S_LINK_UP;
1038 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1039 }
1040 if (pState->pDrv)
1041 pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
1042 }
1043 return VINF_SUCCESS;
1044}
1045
1046static DECLCALLBACK(void) vnetReceive(void *pvState, PVQUEUE pQueue)
1047{
1048 VNETSTATE *pState = (VNETSTATE*)pvState;
1049}
1050
1051static DECLCALLBACK(void) vnetTransmit(void *pvState, PVQUEUE pQueue)
1052{
1053 VNETSTATE *pState = (VNETSTATE*)pvState;
1054}
1055
1056static DECLCALLBACK(void) vnetControl(void *pvState, PVQUEUE pQueue)
1057{
1058 VNETSTATE *pState = (VNETSTATE*)pvState;
1059}
1060
1061
1062/**
1063 * Construct a device instance for a VM.
1064 *
1065 * @returns VBox status.
1066 * @param pDevIns The device instance data.
1067 * If the registration structure is needed, pDevIns->pDevReg points to it.
1068 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1069 * The device number is also found in pDevIns->iInstance, but since it's
1070 * likely to be freqently used PDM passes it as parameter.
1071 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1072 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1073 * iInstance it's expected to be used a bit in this function.
1074 * @thread EMT
1075 */
1076static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1077{
1078 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1079 int rc;
1080
1081 /* Initialize PCI part first. */
1082 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1083 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance, VIRTIO_NET_ID, sizeof(VNetPCIConfig));
1084 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetReceive);
1085 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetTransmit);
1086 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetControl);
1087
1088 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1089
1090 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1091
1092 /*
1093 * Validate configuration.
1094 */
1095 if (!CFGMR3AreValuesValid(pCfgHandle, "MAC\0" "CableConnected\0" "LineSpeed\0"))
1096 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1097 N_("Invalid configuraton for VirtioNet device"));
1098
1099 /* Get config params */
1100 rc = CFGMR3QueryBytes(pCfgHandle, "MAC", pState->config.mac.au8,
1101 sizeof(pState->config.mac.au8));
1102 if (RT_FAILURE(rc))
1103 return PDMDEV_SET_ERROR(pDevIns, rc,
1104 N_("Configuration error: Failed to get MAC address"));
1105 rc = CFGMR3QueryBool(pCfgHandle, "CableConnected", &pState->fCableConnected);
1106 if (RT_FAILURE(rc))
1107 return PDMDEV_SET_ERROR(pDevIns, rc,
1108 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1109
1110 /* Initialize state structure */
1111 pState->fLocked = false;
1112 pState->u32PktNo = 1;
1113
1114 /* Interfaces */
1115 pState->INetworkPort.pfnWaitReceiveAvail = vnetWaitReceiveAvail;
1116 pState->INetworkPort.pfnReceive = vnetReceive;
1117 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1118 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1119 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1120
1121 /* Register save/restore state handlers. */
1122 // TODO:
1123 /*
1124 rc = PDMDevHlpSSMRegisterEx(pDevIns, VNET_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
1125 NULL, NULL, NULL,
1126 NULL, vnetSaveExec, NULL,
1127 NULL, vnetLoadExec, vnetLoadDone);
1128 if (RT_FAILURE(rc))
1129 return rc;*/
1130
1131
1132 /* Create the RX notifier signaller. */
1133 rc = PDMDevHlpPDMQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
1134 vnetCanRxQueueConsumer, true, "VNet-rcv", &pState->pCanRxQueueR3);
1135 if (RT_FAILURE(rc))
1136 return rc;
1137 pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
1138 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1139
1140 /* Create Link Up Timer */
1141 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
1142 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1143 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
1144 if (RT_FAILURE(rc))
1145 return rc;
1146
1147 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1148 if (RT_SUCCESS(rc))
1149 {
1150 if (rc == VINF_NAT_DNS)
1151 {
1152 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1153 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"));
1154 }
1155 pState->pDrv = (PPDMINETWORKCONNECTOR)
1156 pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
1157 if (!pState->pDrv)
1158 {
1159 AssertMsgFailed(("%s Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
1160 return VERR_PDM_MISSING_INTERFACE_BELOW;
1161 }
1162 }
1163 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1164 {
1165 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
1166 }
1167 else
1168 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
1169
1170 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
1171 if (RT_FAILURE(rc))
1172 return rc;
1173
1174 vnetReset(pState);
1175
1176 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
1177 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
1178#if defined(VBOX_WITH_STATISTICS)
1179 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
1180 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
1181 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
1182 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
1183 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
1184#endif /* VBOX_WITH_STATISTICS */
1185
1186 return VINF_SUCCESS;
1187}
1188
1189/**
1190 * Destruct a device instance.
1191 *
1192 * We need to free non-VM resources only.
1193 *
1194 * @returns VBox status.
1195 * @param pDevIns The device instance data.
1196 * @thread EMT
1197 */
1198static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1199{
1200 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1201
1202 Log(("%s Destroying instance\n", INSTANCE(pState)));
1203 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1204 {
1205 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1206 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1207 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1208 }
1209
1210 return vpciDestruct(&pState->VPCI);
1211}
1212
1213/**
1214 * Device relocation callback.
1215 *
1216 * When this callback is called the device instance data, and if the
1217 * device have a GC component, is being relocated, or/and the selectors
1218 * have been changed. The device must use the chance to perform the
1219 * necessary pointer relocations and data updates.
1220 *
1221 * Before the GC code is executed the first time, this function will be
1222 * called with a 0 delta so GC pointer calculations can be one in one place.
1223 *
1224 * @param pDevIns Pointer to the device instance.
1225 * @param offDelta The relocation delta relative to the old location.
1226 *
1227 * @remark A relocation CANNOT fail.
1228 */
1229static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1230{
1231 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1232 vpciRelocate(pDevIns, offDelta);
1233 // TBD
1234}
1235
1236/**
1237 * @copydoc FNPDMDEVSUSPEND
1238 */
1239static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1240{
1241 /* Poke thread waiting for buffer space. */
1242 vnetWakeupReceive(pDevIns);
1243}
1244
1245
1246#ifdef VBOX_DYNAMIC_NET_ATTACH
1247/**
1248 * Detach notification.
1249 *
1250 * One port on the network card has been disconnected from the network.
1251 *
1252 * @param pDevIns The device instance.
1253 * @param iLUN The logical unit which is being detached.
1254 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1255 */
1256static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1257{
1258 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1259 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1260
1261 AssertLogRelReturnVoid(iLUN == 0);
1262
1263 vpciCsEnter(&pState->VPCI, VERR_SEM_BUSY);
1264
1265 /*
1266 * Zero some important members.
1267 */
1268 pState->pDrvBase = NULL;
1269 pState->pDrv = NULL;
1270
1271 vpciCsLeave(&pState->VPCI);
1272}
1273
1274
1275/**
1276 * Attach the Network attachment.
1277 *
1278 * One port on the network card has been connected to a network.
1279 *
1280 * @returns VBox status code.
1281 * @param pDevIns The device instance.
1282 * @param iLUN The logical unit which is being attached.
1283 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1284 *
1285 * @remarks This code path is not used during construction.
1286 */
1287static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1288{
1289 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1290 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1291
1292 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1293
1294 vpciCsEnter(&pState->VPCI, VERR_SEM_BUSY);
1295
1296 /*
1297 * Attach the driver.
1298 */
1299 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1300 if (RT_SUCCESS(rc))
1301 {
1302 if (rc == VINF_NAT_DNS)
1303 {
1304#ifdef RT_OS_LINUX
1305 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1306 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"));
1307#else
1308 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1309 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"));
1310#endif
1311 }
1312 pState->pDrv = (PPDMINETWORKCONNECTOR)pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
1313 if (!pState->pDrv)
1314 {
1315 AssertMsgFailed(("Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
1316 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
1317 }
1318 }
1319 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1320 Log(("%s No attached driver!\n", INSTANCE(pState)));
1321
1322
1323 /*
1324 * Temporary set the link down if it was up so that the guest
1325 * will know that we have change the configuration of the
1326 * network card
1327 */
1328 if ((STATUS & NET_S_LINK_UP) && RT_SUCCESS(rc))
1329 {
1330 STATUS &= ~NET_S_LINK_UP;
1331 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1332 /* Restore the link back in 5 seconds. */
1333 vpciArmTimer(&pState->VPCI, pState->pLinkUpTimer, 5000000);
1334 }
1335
1336 vpciCsLeave(&pState->VPCI);
1337 return rc;
1338
1339}
1340#endif /* VBOX_DYNAMIC_NET_ATTACH */
1341
1342
1343/**
1344 * @copydoc FNPDMDEVPOWEROFF
1345 */
1346static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1347{
1348 /* Poke thread waiting for buffer space. */
1349 vnetWakeupReceive(pDevIns);
1350}
1351
1352/**
1353 * The device registration structure.
1354 */
1355const PDMDEVREG g_DeviceVirtioNet =
1356{
1357 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
1358 PDM_DEVREG_VERSION,
1359 /* Device name. */
1360 "virtio-net",
1361 /* Name of guest context module (no path).
1362 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1363 "VBoxDDGC.gc",
1364 /* Name of ring-0 module (no path).
1365 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1366 "VBoxDDR0.r0",
1367 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
1368 * remain unchanged from registration till VM destruction. */
1369 "Virtio Ethernet.\n",
1370
1371 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
1372 PDM_DEVREG_FLAGS_DEFAULT_BITS, // | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1373 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
1374 PDM_DEVREG_CLASS_NETWORK,
1375 /* Maximum number of instances (per VM). */
1376 8,
1377 /* Size of the instance data. */
1378 sizeof(VNETSTATE),
1379
1380 /* Construct instance - required. */
1381 vnetConstruct,
1382 /* Destruct instance - optional. */
1383 vnetDestruct,
1384 /* Relocation command - optional. */
1385 vnetRelocate,
1386 /* I/O Control interface - optional. */
1387 NULL,
1388 /* Power on notification - optional. */
1389 NULL,
1390 /* Reset notification - optional. */
1391 NULL,
1392 /* Suspend notification - optional. */
1393 vnetSuspend,
1394 /* Resume notification - optional. */
1395 NULL,
1396#ifdef VBOX_DYNAMIC_NET_ATTACH
1397 /* Attach command - optional. */
1398 vnetAttach,
1399 /* Detach notification - optional. */
1400 vnetDetach,
1401#else /* !VBOX_DYNAMIC_NET_ATTACH */
1402 /* Attach command - optional. */
1403 NULL,
1404 /* Detach notification - optional. */
1405 NULL,
1406#endif /* !VBOX_DYNAMIC_NET_ATTACH */
1407 /* Query a LUN base interface - optional. */
1408 NULL,
1409 /* Init complete notification - optional. */
1410 NULL,
1411 /* Power off notification - optional. */
1412 vnetPowerOff,
1413 /* pfnSoftReset */
1414 NULL,
1415 /* u32VersionEnd */
1416 PDM_DEVREG_VERSION
1417};
1418
1419#endif /* IN_RING3 */
1420#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1421
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