VirtualBox

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

Last change on this file since 23515 was 23498, checked in by vboxsync, 16 years ago

#3987: Virtio: Reception/transmission works.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.6 KB
Line 
1/* $Id: DevVirtioNet.cpp 23498 2009-10-02 06:14:07Z 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
46#define VIRTIO_RELOCATE(p, o) *(RTHCUINTPTR *)&p += o
47
48#ifdef DEBUG
49#define QUEUENAME(s, q) g_VPCIDevices[s->enmDevType].pfnGetQueueName(s, q)
50#endif /* DEBUG */
51
52//- TODO: Move to Virtio.h ----------------------------------------------------
53
54#define VPCI_F_NOTIFY_ON_EMPTY 0x1000000
55
56#define VRINGDESC_MAX_SIZE (2 * 1024 * 1024)
57#define VRINGDESC_F_NEXT 0x01
58#define VRINGDESC_F_WRITE 0x02
59
60struct VRingDesc
61{
62 uint64_t u64Addr;
63 uint32_t uLen;
64 uint16_t u16Flags;
65 uint16_t u16Next;
66};
67typedef struct VRingDesc VRINGDESC;
68typedef VRINGDESC *PVRINGDESC;
69
70#define VRINGAVAIL_F_NO_NOTIFY 0x01
71
72struct VRingAvail
73{
74 uint16_t uFlags;
75 uint16_t uNextFreeIndex;
76 uint16_t auRing[1];
77};
78typedef struct VRingAvail VRINGAVAIL;
79
80struct VRingUsedElem
81{
82 uint32_t uId;
83 uint32_t uLen;
84};
85typedef struct VRingUsedElem VRINGUSEDELEM;
86
87#define VRINGUSED_F_NO_INTERRUPT 0x01
88
89struct VRingUsed
90{
91 uint16_t uFlags;
92 uint16_t uIndex;
93 VRINGUSEDELEM aRing[1];
94};
95typedef struct VRingUsed VRINGUSED;
96typedef VRINGUSED *PVRINGUSED;
97
98#define VRING_MAX_SIZE 1024
99
100struct VRing
101{
102 uint16_t uSize;
103 RTGCPHYS addrDescriptors;
104 RTGCPHYS addrAvail;
105 RTGCPHYS addrUsed;
106};
107typedef struct VRing VRING;
108typedef VRING *PVRING;
109
110struct VQueue
111{
112 VRING VRing;
113 uint16_t uNextAvailIndex;
114 uint16_t uNextUsedIndex;
115 uint16_t uPageNumber;
116 void (*pfnCallback)(void *pvState, struct VQueue *pQueue);
117};
118typedef struct VQueue VQUEUE;
119typedef VQUEUE *PVQUEUE;
120
121struct VQueueElemSeg
122{
123 RTGCPHYS addr;
124 void *pv;
125 uint32_t cb;
126};
127typedef struct VQueueElemSeg VQUEUESEG;
128
129struct VQueueElem
130{
131 uint32_t uIndex;
132 uint32_t nIn;
133 uint32_t nOut;
134 VQUEUESEG aSegsIn[VRING_MAX_SIZE];
135 VQUEUESEG aSegsOut[VRING_MAX_SIZE];
136};
137typedef struct VQueueElem VQUEUEELEM;
138typedef VQUEUEELEM *PVQUEUEELEM;
139
140
141enum VirtioDeviceType
142{
143 VIRTIO_NET_ID = 0,
144 VIRTIO_BLK_ID = 1
145};
146
147struct VPCIState_st
148{
149 PDMCRITSECT cs; /**< Critical section - what is it protecting? */
150 /* Read-only part, never changes after initialization. */
151 VirtioDeviceType enmDevType; /**< Device type: net or blk. */
152 char szInstance[8]; /**< Instance name, e.g. VNet#1. */
153
154 PDMIBASE IBase;
155 PDMILEDPORTS ILeds; /**< LED interface */
156 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
157
158 PPDMDEVINSR3 pDevInsR3; /**< Device instance - R3. */
159 PPDMDEVINSR0 pDevInsR0; /**< Device instance - R0. */
160 PPDMDEVINSRC pDevInsRC; /**< Device instance - RC. */
161
162 /** TODO */
163 PCIDEVICE pciDevice;
164 /** Base port of I/O space region. */
165 RTIOPORT addrIOPort;
166
167 /* Read/write part, protected with critical section. */
168 /** Status LED. */
169 PDMLED led;
170
171 uint32_t uGuestFeatures;
172 uint16_t uQueueSelector; /**< An index in aQueues array. */
173 uint8_t uStatus; /**< Device Status (bits are device-specific). */
174 uint8_t uISR; /**< Interrupt Status Register. */
175 PVQUEUE pQueues;
176
177#if defined(VBOX_WITH_STATISTICS)
178 STAMPROFILEADV StatIOReadGC;
179 STAMPROFILEADV StatIOReadHC;
180 STAMPROFILEADV StatIOWriteGC;
181 STAMPROFILEADV StatIOWriteHC;
182 STAMCOUNTER StatIntsRaised;
183#endif /* VBOX_WITH_STATISTICS */
184};
185typedef struct VPCIState_st VPCISTATE;
186typedef VPCISTATE *PVPCISTATE;
187
188//- TODO: Move to VirtioPCI.cpp -----------------------------------------------
189
190/*****************************************************************************/
191RT_C_DECLS_BEGIN
192PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pState);
193PDMBOTHCBDECL(int) vnetGetConfig(void *pState, uint32_t port, uint32_t cb, void *data);
194PDMBOTHCBDECL(int) vnetSetConfig(void *pState, uint32_t port, uint32_t cb, void *data);
195PDMBOTHCBDECL(void) vnetReset(void *pState);
196#ifdef DEBUG
197static const char *vnetGetQueueName(void *pvState, PVQUEUE pQueue);
198#endif /* DEBUG */
199RT_C_DECLS_END
200
201/*****************************************************************************/
202
203struct VirtioPCIDevices
204{
205 uint16_t uPCIVendorId;
206 uint16_t uPCIDeviceId;
207 uint16_t uPCISubsystemVendorId;
208 uint16_t uPCISubsystemId;
209 uint16_t uPCIClass;
210 unsigned nQueues;
211 const char *pcszName;
212 const char *pcszNameFmt;
213 uint32_t (*pfnGetHostFeatures)(void *pvState);
214 int (*pfnGetConfig)(void *pvState, uint32_t port, uint32_t cb, void *data);
215 int (*pfnSetConfig)(void *pvState, uint32_t port, uint32_t cb, void *data);
216 void (*pfnReset)(void *pvState);
217#ifdef DEBUG
218 const char *(*pfnGetQueueName)(void *pvState, PVQUEUE pQueue);
219#endif /* DEBUG */
220} g_VPCIDevices[] =
221{
222 /* Vendor Device SSVendor SubSys Class NQ Name Instance */
223 { /* Virtio Network Device */
224 0x1AF4, 0x1000, 0x1AF4, 1 + VIRTIO_NET_ID, 0x0200, 3, "virtio-net", "vnet%d",
225 vnetGetHostFeatures, vnetGetConfig, vnetSetConfig, vnetReset
226#ifdef DEBUG
227 , vnetGetQueueName
228#endif /* DEBUG */
229 },
230 { /* Virtio Block Device */
231 0x1AF4, 0x1001, 0x1AF4, 1 + VIRTIO_BLK_ID, 0x0180, 2, "virtio-blk", "vblk%d",
232 NULL, NULL, NULL, NULL
233#ifdef DEBUG
234 , NULL
235#endif /* DEBUG */
236 },
237};
238
239
240/*****************************************************************************/
241
242#define VPCI_HOST_FEATURES 0x0
243#define VPCI_GUEST_FEATURES 0x4
244#define VPCI_QUEUE_PFN 0x8
245#define VPCI_QUEUE_NUM 0xC
246#define VPCI_QUEUE_SEL 0xE
247#define VPCI_QUEUE_NOTIFY 0x10
248#define VPCI_STATUS 0x12
249#define VPCI_ISR 0x13
250#define VPCI_ISR_QUEUE 0x1
251#define VPCI_ISR_CONFIG 0x3
252#define VPCI_CONFIG 0x14
253
254#define VPCI_STATUS_ACK 0x01
255#define VPCI_STATUS_DRV 0x02
256#define VPCI_STATUS_DRV_OK 0x04
257#define VPCI_STATUS_FAILED 0x80
258
259/** @todo use+extend RTNETIPV4 */
260
261/** @todo use+extend RTNETTCP */
262
263#ifndef VBOX_DEVICE_STRUCT_TESTCASE
264
265/* Forward declarations ******************************************************/
266RT_C_DECLS_BEGIN
267PDMBOTHCBDECL(int) vpciIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
268PDMBOTHCBDECL(int) vpciIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
269PDMBOTHCBDECL(int) vpciRaiseInterrupt(VPCISTATE *pState, int rcBusy, uint8_t u8IntCause);
270RT_C_DECLS_END
271
272
273static void vqueueReset(PVQUEUE pQueue)
274{
275 pQueue->VRing.addrDescriptors = 0;
276 pQueue->VRing.addrAvail = 0;
277 pQueue->VRing.addrUsed = 0;
278 pQueue->uNextAvailIndex = 0;
279 pQueue->uNextUsedIndex = 0;
280}
281
282static void vqueueInit(PVQUEUE pQueue, uint32_t uPageNumber)
283{
284 pQueue->VRing.addrDescriptors = uPageNumber << PAGE_SHIFT;
285 pQueue->VRing.addrAvail = pQueue->VRing.addrDescriptors
286 + sizeof(VRINGDESC) * pQueue->VRing.uSize;
287 pQueue->VRing.addrUsed = RT_ALIGN(
288 pQueue->VRing.addrAvail + RT_OFFSETOF(VRINGAVAIL, auRing[pQueue->VRing.uSize]),
289 PAGE_SIZE); /* The used ring must start from the next page. */
290 pQueue->uNextAvailIndex = 0;
291 pQueue->uNextUsedIndex = 0;
292}
293
294uint16_t vringReadAvailIndex(PVPCISTATE pState, PVRING pVRing)
295{
296 uint16_t tmp;
297
298 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
299 pVRing->addrAvail + RT_OFFSETOF(VRINGAVAIL, uNextFreeIndex),
300 &tmp, sizeof(tmp));
301 return tmp;
302}
303
304DECLINLINE(bool) vqueueIsReady(PVPCISTATE pState, PVQUEUE pQueue)
305{
306 return !!pQueue->VRing.addrAvail;
307}
308
309DECLINLINE(bool) vqueueIsEmpty(PVPCISTATE pState, PVQUEUE pQueue)
310{
311 return (vringReadAvailIndex(pState, &pQueue->VRing) == pQueue->uNextAvailIndex);
312}
313
314void vqueueElemFree(PVQUEUEELEM pElem)
315{
316}
317
318void vringReadDesc(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex, PVRINGDESC pDesc)
319{
320 //Log(("%s vringReadDesc: ring=%p idx=%u\n", INSTANCE(pState), pVRing, uIndex));
321 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
322 pVRing->addrDescriptors + sizeof(VRINGDESC) * (uIndex % pVRing->uSize),
323 pDesc, sizeof(VRINGDESC));
324}
325
326static uint16_t vringReadAvail(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex)
327{
328 uint16_t tmp;
329
330 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
331 pVRing->addrAvail + RT_OFFSETOF(VRINGAVAIL, auRing[uIndex % pVRing->uSize]),
332 &tmp, sizeof(tmp));
333 return tmp;
334}
335
336bool vqueueGet(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem)
337{
338 if (vqueueIsEmpty(pState, pQueue))
339 return false;
340
341 pElem->nIn = pElem->nOut = 0;
342
343 Log2(("%s vqueueGet: %s avail_idx=%u\n", INSTANCE(pState),
344 QUEUENAME(pState, pQueue), pQueue->uNextAvailIndex));
345
346 VRINGDESC desc;
347 uint16_t idx = vringReadAvail(pState, &pQueue->VRing, pQueue->uNextAvailIndex++);
348 pElem->uIndex = idx;
349 do
350 {
351 VQUEUESEG *pSeg;
352
353 vringReadDesc(pState, &pQueue->VRing, idx, &desc);
354 if (desc.u16Flags & VRINGDESC_F_WRITE)
355 {
356 Log2(("%s vqueueGet: %s IN seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pState),
357 QUEUENAME(pState, pQueue), pElem->nIn, idx, desc.u64Addr, desc.uLen));
358 pSeg = &pElem->aSegsIn[pElem->nIn++];
359 }
360 else
361 {
362 Log2(("%s vqueueGet: %s OUT seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pState),
363 QUEUENAME(pState, pQueue), pElem->nOut, idx, desc.u64Addr, desc.uLen));
364 pSeg = &pElem->aSegsOut[pElem->nOut++];
365 }
366
367 pSeg->addr = desc.u64Addr;
368 pSeg->cb = desc.uLen;
369 pSeg->pv = NULL;
370
371 idx = desc.u16Next;
372 } while (desc.u16Flags & VRINGDESC_F_NEXT);
373
374 Log2(("%s vqueueGet: %s head_desc_idx=%u nIn=%u nOut=%u\n", INSTANCE(pState),
375 QUEUENAME(pState, pQueue), pElem->uIndex, pElem->nIn, pElem->nOut));
376 return true;
377}
378
379uint16_t vringReadUsedIndex(PVPCISTATE pState, PVRING pVRing)
380{
381 uint16_t tmp;
382 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
383 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uIndex),
384 &tmp, sizeof(tmp));
385 return tmp;
386}
387
388void vringWriteUsedIndex(PVPCISTATE pState, PVRING pVRing, uint16_t u16Value)
389{
390 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns),
391 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uIndex),
392 &u16Value, sizeof(u16Value));
393}
394
395void vringWriteUsedElem(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex, uint32_t uId, uint32_t uLen)
396{
397 VRINGUSEDELEM elem;
398
399 elem.uId = uId;
400 elem.uLen = uLen;
401 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns),
402 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, aRing[uIndex % pVRing->uSize]),
403 &elem, sizeof(elem));
404}
405
406void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen)
407{
408 unsigned int i, uOffset;
409
410 Log2(("%s vqueuePut: %s desc_idx=%u acb=%u\n", INSTANCE(pState),
411 QUEUENAME(pState, pQueue), pElem->uIndex, uLen));
412 for (i = uOffset = 0; i < pElem->nIn && uOffset < uLen; i++)
413 {
414 uint32_t cbSegLen = RT_MIN(uLen - uOffset, pElem->aSegsIn[i].cb);
415 if (pElem->aSegsIn[i].pv)
416 {
417 Log2(("%s vqueuePut: %s used_idx=%u seg=%u addr=%p pv=%p cb=%u acb=%u\n", INSTANCE(pState),
418 QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, i, pElem->aSegsIn[i].addr, pElem->aSegsIn[i].pv, pElem->aSegsIn[i].cb, cbSegLen));
419 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns), pElem->aSegsIn[i].addr,
420 pElem->aSegsIn[i].pv, cbSegLen);
421 }
422 uOffset += cbSegLen;
423 }
424
425 Log2(("%s vqueuePut: %s used_idx=%u guest_used_idx=%u id=%u len=%u\n", INSTANCE(pState),
426 QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, vringReadUsedIndex(pState, &pQueue->VRing), pElem->uIndex, uLen));
427 vringWriteUsedElem(pState, &pQueue->VRing, pQueue->uNextUsedIndex++, pElem->uIndex, uLen);
428}
429
430void vqueueNotify(PVPCISTATE pState, PVQUEUE pQueue)
431{
432 int rc = vpciRaiseInterrupt(pState, VERR_INTERNAL_ERROR, VPCI_ISR_QUEUE);
433 if (RT_FAILURE(rc))
434 Log(("%s vqueueNotify: Failed to raise an interrupt (%Vrc).\n", INSTANCE(pState), rc));
435}
436
437void vqueueSync(PVPCISTATE pState, PVQUEUE pQueue)
438{
439 Log2(("%s vqueueSync: %s old_used_idx=%u new_used_idx=%u\n", INSTANCE(pState),
440 QUEUENAME(pState, pQueue), vringReadUsedIndex(pState, &pQueue->VRing), pQueue->uNextUsedIndex));
441 vringWriteUsedIndex(pState, &pQueue->VRing, pQueue->uNextUsedIndex);
442 vqueueNotify(pState, pQueue);
443}
444
445void vpciReset(PVPCISTATE pState)
446{
447 pState->uGuestFeatures = 0;
448 pState->uQueueSelector = 0;
449 pState->uStatus = 0;
450 pState->uISR = 0;
451
452 for (unsigned i = 0; i < g_VPCIDevices[pState->enmDevType].nQueues; i++)
453 vqueueReset(&pState->pQueues[i]);
454}
455
456/**
457 * Arm a timer.
458 *
459 * @param pState Pointer to the device state structure.
460 * @param pTimer Pointer to the timer.
461 * @param uExpireIn Expiration interval in microseconds.
462 */
463DECLINLINE(void) vpciArmTimer(VPCISTATE *pState, PTMTIMER pTimer, uint32_t uExpireIn)
464{
465 Log2(("%s Arming timer to fire in %d usec...\n",
466 INSTANCE(pState), uExpireIn));
467 TMTimerSet(pTimer, TMTimerFromMicro(pTimer, uExpireIn) +
468 TMTimerGet(pTimer));
469}
470
471
472DECLINLINE(int) vpciCsEnter(VPCISTATE *pState, int iBusyRc)
473{
474 return PDMCritSectEnter(&pState->cs, iBusyRc);
475}
476
477DECLINLINE(void) vpciCsLeave(VPCISTATE *pState)
478{
479 PDMCritSectLeave(&pState->cs);
480}
481
482/**
483 * Raise interrupt.
484 *
485 * @param pState The device state structure.
486 * @param rcBusy Status code to return when the critical section is busy.
487 * @param u8IntCause Interrupt cause bit mask to set in PCI ISR port.
488 */
489PDMBOTHCBDECL(int) vpciRaiseInterrupt(VPCISTATE *pState, int rcBusy, uint8_t u8IntCause)
490{
491 int rc = vpciCsEnter(pState, rcBusy);
492 if (RT_UNLIKELY(rc != VINF_SUCCESS))
493 return rc;
494
495 pState->uISR |= u8IntCause;
496 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 1);
497 vpciCsLeave(pState);
498 return VINF_SUCCESS;
499}
500
501/**
502 * Lower interrupt.
503 *
504 * @param pState The device state structure.
505 */
506PDMBOTHCBDECL(void) vpciLowerInterrupt(VPCISTATE *pState)
507{
508 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 0);
509}
510
511/**
512 * Port I/O Handler for IN operations.
513 *
514 * @returns VBox status code.
515 *
516 * @param pDevIns The device instance.
517 * @param pvUser Pointer to the device state structure.
518 * @param port Port number used for the IN operation.
519 * @param pu32 Where to store the result.
520 * @param cb Number of bytes read.
521 * @thread EMT
522 */
523PDMBOTHCBDECL(int) vpciIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
524 RTIOPORT port, uint32_t *pu32, unsigned cb)
525{
526 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
527 int rc = VINF_SUCCESS;
528 const char *szInst = INSTANCE(pState);
529 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIORead), a);
530
531 port -= pState->addrIOPort;
532 switch (port)
533 {
534 case VPCI_HOST_FEATURES:
535 /* Tell the guest what features we support. */
536 *pu32 = g_VPCIDevices[pState->enmDevType].pfnGetHostFeatures(pState);
537 break;
538
539 case VPCI_GUEST_FEATURES:
540 *pu32 = pState->uGuestFeatures;
541 break;
542
543 case VPCI_QUEUE_PFN:
544 *pu32 = pState->pQueues[pState->uQueueSelector].uPageNumber;
545 break;
546
547 case VPCI_QUEUE_NUM:
548 Assert(cb == 2);
549 *(uint16_t*)pu32 = pState->pQueues[pState->uQueueSelector].VRing.uSize;
550 break;
551
552 case VPCI_QUEUE_SEL:
553 Assert(cb == 2);
554 *(uint16_t*)pu32 = pState->uQueueSelector;
555 break;
556
557 case VPCI_STATUS:
558 Assert(cb == 1);
559 *(uint8_t*)pu32 = pState->uStatus;
560 break;
561
562 case VPCI_ISR:
563 Assert(cb == 1);
564 *(uint8_t*)pu32 = pState->uISR;
565 pState->uISR = 0; /* read clears all interrupts */
566 vpciLowerInterrupt(pState);
567 break;
568
569 default:
570 if (port >= VPCI_CONFIG)
571 rc = g_VPCIDevices[pState->enmDevType].pfnGetConfig(pState, port - VPCI_CONFIG, cb, pu32);
572 else
573 {
574 *pu32 = 0xFFFFFFFF;
575 rc = PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "%s virtioIOPortIn: no valid port at offset port=%RTiop cb=%08x\n", szInst, port, cb);
576 }
577 break;
578 }
579 Log3(("%s virtioIOPortIn: At %RTiop in %0*x\n", szInst, port, cb*2, *pu32));
580 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIORead), a);
581 return rc;
582}
583
584
585/**
586 * Port I/O Handler for OUT operations.
587 *
588 * @returns VBox status code.
589 *
590 * @param pDevIns The device instance.
591 * @param pvUser User argument.
592 * @param Port Port number used for the IN operation.
593 * @param u32 The value to output.
594 * @param cb The value size in bytes.
595 * @thread EMT
596 */
597PDMBOTHCBDECL(int) vpciIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
598 RTIOPORT port, uint32_t u32, unsigned cb)
599{
600 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
601 int rc = VINF_SUCCESS;
602 const char *szInst = INSTANCE(pState);
603 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIOWrite), a);
604
605 port -= pState->addrIOPort;
606 Log3(("%s virtioIOPortOut: At %RTiop out %0*x\n", szInst, port, cb*2, u32));
607
608 switch (port)
609 {
610 case VPCI_GUEST_FEATURES:
611 // TODO: Feature negotiation code goes here.
612 // The guest may potentially desire features we don't support!
613 pState->uGuestFeatures = u32;
614 break;
615
616 case VPCI_QUEUE_PFN:
617 /*
618 * The guest is responsible for allocating the pages for queues,
619 * here it provides us with the page number of descriptor table.
620 * Note that we provide the size of the queue to the guest via
621 * VIRTIO_PCI_QUEUE_NUM.
622 */
623 pState->pQueues[pState->uQueueSelector].uPageNumber = u32;
624 if (u32)
625 vqueueInit(&pState->pQueues[pState->uQueueSelector], u32);
626 else
627 g_VPCIDevices[pState->enmDevType].pfnReset(pState);
628 break;
629
630 case VPCI_QUEUE_SEL:
631 Assert(cb == 2);
632 u32 &= 0xFFFF;
633 if (u32 < g_VPCIDevices[pState->enmDevType].nQueues)
634 pState->uQueueSelector = u32;
635 else
636 Log3(("%s virtioIOPortOut: Invalid queue selector %08x\n", szInst, u32));
637 break;
638
639 case VPCI_QUEUE_NOTIFY:
640 Assert(cb == 2);
641 u32 &= 0xFFFF;
642 if (u32 < g_VPCIDevices[pState->enmDevType].nQueues)
643 if (pState->pQueues[u32].VRing.addrDescriptors)
644 pState->pQueues[u32].pfnCallback(pState, &pState->pQueues[u32]);
645 else
646 Log(("%s The queue (#%d) being notified has not been initialized.\n",
647 INSTANCE(pState), u32));
648 else
649 Log(("%s Invalid queue number (%d)\n", INSTANCE(pState), u32));
650 break;
651
652 case VPCI_STATUS:
653 Assert(cb == 1);
654 u32 &= 0xFF;
655 pState->uStatus = u32;
656 /* Writing 0 to the status port triggers device reset. */
657 if (u32 == 0)
658 g_VPCIDevices[pState->enmDevType].pfnReset(pState);
659 break;
660
661 default:
662 if (port >= VPCI_CONFIG)
663 rc = g_VPCIDevices[pState->enmDevType].pfnSetConfig(pState, port - VPCI_CONFIG, cb, &u32);
664 else
665 rc = PDMDeviceDBGFStop(pDevIns, RT_SRC_POS, "%s virtioIOPortOut: no valid port at offset port=%RTiop cb=%08x\n", szInst, port, cb);
666 break;
667 }
668
669 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIOWrite), a);
670 return rc;
671}
672
673#ifdef IN_RING3
674// Common
675/**
676 * Map PCI I/O region.
677 *
678 * @return VBox status code.
679 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
680 * @param iRegion The region number.
681 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
682 * I/O port, else it's a physical address.
683 * This address is *NOT* relative to pci_mem_base like earlier!
684 * @param cb Region size.
685 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
686 * @thread EMT
687 */
688static DECLCALLBACK(int) vpciMap(PPCIDEVICE pPciDev, int iRegion,
689 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
690{
691 int rc;
692 VPCISTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VPCISTATE*);
693
694 if (enmType != PCI_ADDRESS_SPACE_IO)
695 {
696 /* We should never get here */
697 AssertMsgFailed(("Invalid PCI address space param in map callback"));
698 return VERR_INTERNAL_ERROR;
699 }
700
701 pState->addrIOPort = (RTIOPORT)GCPhysAddress;
702 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
703 vpciIOPortOut, vpciIOPortIn, NULL, NULL, "VirtioNet");
704#if 0
705 AssertRCReturn(rc, rc);
706 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
707 "vpciIOPortOut", "vpciIOPortIn", NULL, NULL, "VirtioNet");
708 AssertRCReturn(rc, rc);
709 rc = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, pState->addrIOPort, cb, 0,
710 "vpciIOPortOut", "vpciIOPortIn", NULL, NULL, "VirtioNet");
711#endif
712 AssertRC(rc);
713 return rc;
714}
715
716/**
717 * Provides interfaces to the driver.
718 *
719 * @returns Pointer to interface. NULL if the interface is not supported.
720 * @param pInterface Pointer to this interface structure.
721 * @param enmInterface The requested interface identification.
722 * @thread EMT
723 */
724static DECLCALLBACK(void *) vpciQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
725{
726 VPCISTATE *pState = IFACE_TO_STATE(pInterface, IBase);
727 Assert(&pState->IBase == pInterface);
728 switch (enmInterface)
729 {
730 case PDMINTERFACE_BASE:
731 return &pState->IBase;
732 case PDMINTERFACE_LED_PORTS:
733 return &pState->ILeds;
734 default:
735 return NULL;
736 }
737}
738
739/**
740 * Gets the pointer to the status LED of a unit.
741 *
742 * @returns VBox status code.
743 * @param pInterface Pointer to the interface structure.
744 * @param iLUN The unit which status LED we desire.
745 * @param ppLed Where to store the LED pointer.
746 * @thread EMT
747 */
748static DECLCALLBACK(int) vpciQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
749{
750 VPCISTATE *pState = IFACE_TO_STATE(pInterface, ILeds);
751 int rc = VERR_PDM_LUN_NOT_FOUND;
752
753 if (iLUN == 0)
754 {
755 *ppLed = &pState->led;
756 rc = VINF_SUCCESS;
757 }
758 return rc;
759}
760
761/**
762 * Sets 8-bit register in PCI configuration space.
763 * @param refPciDev The PCI device.
764 * @param uOffset The register offset.
765 * @param u16Value The value to store in the register.
766 * @thread EMT
767 */
768DECLINLINE(void) vpciCfgSetU8(PCIDEVICE& refPciDev, uint32_t uOffset, uint8_t u8Value)
769{
770 Assert(uOffset < sizeof(refPciDev.config));
771 refPciDev.config[uOffset] = u8Value;
772}
773
774/**
775 * Sets 16-bit register in PCI configuration space.
776 * @param refPciDev The PCI device.
777 * @param uOffset The register offset.
778 * @param u16Value The value to store in the register.
779 * @thread EMT
780 */
781DECLINLINE(void) vpciCfgSetU16(PCIDEVICE& refPciDev, uint32_t uOffset, uint16_t u16Value)
782{
783 Assert(uOffset+sizeof(u16Value) <= sizeof(refPciDev.config));
784 *(uint16_t*)&refPciDev.config[uOffset] = u16Value;
785}
786
787/**
788 * Sets 32-bit register in PCI configuration space.
789 * @param refPciDev The PCI device.
790 * @param uOffset The register offset.
791 * @param u32Value The value to store in the register.
792 * @thread EMT
793 */
794DECLINLINE(void) vpciCfgSetU32(PCIDEVICE& refPciDev, uint32_t uOffset, uint32_t u32Value)
795{
796 Assert(uOffset+sizeof(u32Value) <= sizeof(refPciDev.config));
797 *(uint32_t*)&refPciDev.config[uOffset] = u32Value;
798}
799
800
801/**
802 * Set PCI configuration space registers.
803 *
804 * @param pci Reference to PCI device structure.
805 * @thread EMT
806 */
807static DECLCALLBACK(void) vpciConfigure(PCIDEVICE& pci, VirtioDeviceType enmType)
808{
809 Assert(enmType < (int)RT_ELEMENTS(g_VPCIDevices));
810 /* Configure PCI Device, assume 32-bit mode ******************************/
811 PCIDevSetVendorId(&pci, g_VPCIDevices[enmType].uPCIVendorId);
812 PCIDevSetDeviceId(&pci, g_VPCIDevices[enmType].uPCIDeviceId);
813 vpciCfgSetU16(pci, VBOX_PCI_SUBSYSTEM_VENDOR_ID, g_VPCIDevices[enmType].uPCISubsystemVendorId);
814 vpciCfgSetU16(pci, VBOX_PCI_SUBSYSTEM_ID, g_VPCIDevices[enmType].uPCISubsystemId);
815
816 /* ABI version, must be equal 0 as of 2.6.30 kernel. */
817 vpciCfgSetU8( pci, VBOX_PCI_REVISION_ID, 0x00);
818 /* Ethernet adapter */
819 vpciCfgSetU8( pci, VBOX_PCI_CLASS_PROG, 0x00);
820 vpciCfgSetU16(pci, VBOX_PCI_CLASS_DEVICE, g_VPCIDevices[enmType].uPCIClass);
821 /* Interrupt Pin: INTA# */
822 vpciCfgSetU8( pci, VBOX_PCI_INTERRUPT_PIN, 0x01);
823}
824
825// TODO: header
826DECLCALLBACK(int) vpciConstruct(PPDMDEVINS pDevIns, VPCISTATE *pState,
827 int iInstance, VirtioDeviceType enmDevType,
828 unsigned uConfigSize)
829{
830 int rc = VINF_SUCCESS;
831 /* Init handles and log related stuff. */
832 RTStrPrintf(pState->szInstance, sizeof(pState->szInstance), g_VPCIDevices[enmDevType].pcszNameFmt, iInstance);
833 pState->enmDevType = enmDevType;
834
835 /* Allocate queues */
836 pState->pQueues = (VQUEUE*)RTMemAllocZ(sizeof(VQUEUE) * g_VPCIDevices[enmDevType].nQueues);
837
838 pState->pDevInsR3 = pDevIns;
839 pState->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
840 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
841 pState->led.u32Magic = PDMLED_MAGIC;
842
843 pState->ILeds.pfnQueryStatusLed = vpciQueryStatusLed;
844
845 /* Initialize critical section. */
846 rc = PDMDevHlpCritSectInit(pDevIns, &pState->cs, pState->szInstance);
847 if (RT_FAILURE(rc))
848 return rc;
849
850 /* Set PCI config registers */
851 vpciConfigure(pState->pciDevice, VIRTIO_NET_ID);
852 /* Register PCI device */
853 rc = PDMDevHlpPCIRegister(pDevIns, &pState->pciDevice);
854 if (RT_FAILURE(rc))
855 return rc;
856
857 /* Map our ports to IO space. */
858 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, VPCI_CONFIG + uConfigSize,
859 PCI_ADDRESS_SPACE_IO, vpciMap);
860 if (RT_FAILURE(rc))
861 return rc;
862
863 /* Status driver */
864 PPDMIBASE pBase;
865 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pState->IBase, &pBase, "Status Port");
866 if (RT_FAILURE(rc))
867 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
868 pState->pLedsConnector = (PPDMILEDCONNECTORS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_LED_CONNECTORS);
869
870#if defined(VBOX_WITH_STATISTICS)
871 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in GC", "/Devices/VNet%d/IO/ReadGC", iInstance);
872 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in HC", "/Devices/VNet%d/IO/ReadHC", iInstance);
873 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in GC", "/Devices/VNet%d/IO/WriteGC", iInstance);
874 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in HC", "/Devices/VNet%d/IO/WriteHC", iInstance);
875 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIntsRaised, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of raised interrupts", "/Devices/VNet%d/Interrupts/Raised", iInstance);
876#endif /* VBOX_WITH_STATISTICS */
877
878 return rc;
879}
880
881/**
882 * Destruct PCI-related part of device.
883 *
884 * We need to free non-VM resources only.
885 *
886 * @returns VBox status.
887 * @param pState The device state structure.
888 */
889static DECLCALLBACK(int) vpciDestruct(VPCISTATE* pState)
890{
891 Log(("%s Destroying PCI instance\n", INSTANCE(pState)));
892
893 if (pState->pQueues)
894 RTMemFree(pState->pQueues);
895
896 if (PDMCritSectIsInitialized(&pState->cs))
897 {
898 PDMR3CritSectDelete(&pState->cs);
899 }
900 return VINF_SUCCESS;
901}
902
903/**
904 * Device relocation callback.
905 *
906 * When this callback is called the device instance data, and if the
907 * device have a GC component, is being relocated, or/and the selectors
908 * have been changed. The device must use the chance to perform the
909 * necessary pointer relocations and data updates.
910 *
911 * Before the GC code is executed the first time, this function will be
912 * called with a 0 delta so GC pointer calculations can be one in one place.
913 *
914 * @param pDevIns Pointer to the device instance.
915 * @param offDelta The relocation delta relative to the old location.
916 *
917 * @remark A relocation CANNOT fail.
918 */
919static DECLCALLBACK(void) vpciRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
920{
921 VPCISTATE* pState = PDMINS_2_DATA(pDevIns, VPCISTATE*);
922 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
923 // TBD
924}
925
926PVQUEUE vpciAddQueue(VPCISTATE* pState, unsigned uSize,
927 void (*pfnCallback)(void *pvState, PVQUEUE pQueue))
928{
929 PVQUEUE pQueue = NULL;
930 /* Find an empty queue slot */
931 for (unsigned i = 0; i < g_VPCIDevices[pState->enmDevType].nQueues; i++)
932 {
933 if (pState->pQueues[i].VRing.uSize == 0)
934 {
935 pQueue = &pState->pQueues[i];
936 break;
937 }
938 }
939
940 if (!pQueue)
941 {
942 Log(("%s Too many queues being added, no empty slots available!\n", INSTANCE(pState)));
943 }
944 else
945 {
946 pQueue->VRing.uSize = uSize;
947 pQueue->VRing.addrDescriptors = 0;
948 pQueue->uPageNumber = 0;
949 pQueue->pfnCallback = pfnCallback;
950 }
951
952 return pQueue;
953}
954
955
956#endif /* IN_RING3 */
957
958
959//------------------------- Tear off here: vnet -------------------------------
960
961//- TODO: Move to VirtioNet.h -------------------------------------------------
962
963#define VNET_MAX_FRAME_SIZE 65536 // TODO: Is it the right limit?
964#define VNET_SAVEDSTATE_VERSION 1
965
966/* Virtio net features */
967#define VNET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
968#define VNET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
969#define VNET_F_MAC 0x00000020 /* Host has given MAC address. */
970#define VNET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
971#define VNET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
972#define VNET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
973#define VNET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
974#define VNET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
975#define VNET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
976#define VNET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
977#define VNET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
978#define VNET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
979#define VNET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
980#define VNET_F_STATUS 0x00010000 /* virtio_net_config.status available */
981#define VNET_F_CTRL_VQ 0x00020000 /* Control channel available */
982#define VNET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
983#define VNET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
984
985#define VNET_S_LINK_UP 1
986
987
988#ifdef _MSC_VER
989struct VNetPCIConfig
990#else /* !_MSC_VER */
991struct __attribute__ ((__packed__)) VNetPCIConfig
992#endif /* !_MSC_VER */
993{
994 RTMAC mac;
995 uint16_t uStatus;
996};
997
998AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
999
1000/**
1001 * Device state structure. Holds the current state of device.
1002 */
1003
1004struct VNetState_st
1005{
1006 /* VPCISTATE must be the first member! */
1007 VPCISTATE VPCI;
1008
1009 PDMINETWORKPORT INetworkPort;
1010 PDMINETWORKCONFIG INetworkConfig;
1011 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
1012 R3PTRTYPE(PPDMINETWORKCONNECTOR) pDrv; /**< Connector of attached network driver. */
1013
1014 PTMTIMERR3 pLinkUpTimer; /**< Link Up(/Restore) Timer. */
1015
1016 /** PCI config area holding MAC address as well as TBD. */
1017 struct VNetPCIConfig config;
1018 /** True if physical cable is attached in configuration. */
1019 bool fCableConnected;
1020
1021 /** Number of packet being sent/received to show in debug log. */
1022 uint32_t u32PktNo;
1023
1024 /** Locked state -- no state alteration possible. */
1025 bool fLocked;
1026
1027 PVQUEUE pRxQueue;
1028 PVQUEUE pTxQueue;
1029 PVQUEUE pCtlQueue;
1030 /* Receive-blocking-related fields ***************************************/
1031
1032 /** N/A: */
1033 bool volatile fMaybeOutOfSpace;
1034 /** EMT: Gets signalled when more RX descriptors become available. */
1035 RTSEMEVENT hEventMoreRxDescAvail;
1036
1037 /* Statistic fields ******************************************************/
1038
1039 STAMCOUNTER StatReceiveBytes;
1040 STAMCOUNTER StatTransmitBytes;
1041#if defined(VBOX_WITH_STATISTICS)
1042 STAMPROFILEADV StatReceive;
1043 STAMPROFILEADV StatTransmit;
1044 STAMPROFILEADV StatTransmitSend;
1045 STAMPROFILE StatRxOverflow;
1046 STAMCOUNTER StatRxOverflowWakeup;
1047#endif /* VBOX_WITH_STATISTICS */
1048
1049};
1050typedef struct VNetState_st VNETSTATE;
1051typedef VNETSTATE *PVNETSTATE;
1052
1053#define VNETHDR_GSO_NONE 0
1054
1055struct VNetHdr
1056{
1057 uint8_t u8Flags;
1058 uint8_t u8GSOType;
1059 uint16_t u16HdrLen;
1060 uint16_t u16GSOSize;
1061 uint16_t u16CSumStart;
1062 uint16_t u16CSumOffset;
1063};
1064typedef struct VNetHdr VNETHDR;
1065typedef VNETHDR *PVNETHDR;
1066
1067AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
1068
1069//- TODO: Leave here ----------------------------------------------------------
1070
1071#undef INSTANCE
1072#define INSTANCE(pState) pState->VPCI.szInstance
1073#undef IFACE_TO_STATE
1074#define IFACE_TO_STATE(pIface, ifaceName) ((VNETSTATE *)((char*)pIface - RT_OFFSETOF(VNETSTATE, ifaceName)))
1075#define STATUS pState->config.uStatus
1076
1077#ifdef DEBUG
1078static const char *vnetGetQueueName(void *pvState, PVQUEUE pQueue)
1079{
1080 VNETSTATE *pState = (VNETSTATE *)pvState;
1081 if (pQueue == pState->pRxQueue)
1082 return "RX ";
1083 else if (pQueue == pState->pTxQueue)
1084 return "TX ";
1085 else if (pQueue == pState->pCtlQueue)
1086 return "CTL";
1087 return "Invalid";
1088}
1089#endif /* DEBUG */
1090
1091PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
1092{
1093 // TODO: implement
1094 return VNET_F_MAC | VNET_F_STATUS;
1095}
1096
1097PDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
1098{
1099 VNETSTATE *pState = (VNETSTATE *)pvState;
1100 if (port + cb > sizeof(struct VNetPCIConfig))
1101 {
1102 Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
1103 return VERR_INTERNAL_ERROR;
1104 }
1105 memcpy(data, ((uint8_t*)&pState->config) + port, cb);
1106 return VINF_SUCCESS;
1107}
1108
1109PDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
1110{
1111 VNETSTATE *pState = (VNETSTATE *)pvState;
1112 if (port + cb > sizeof(struct VNetPCIConfig))
1113 {
1114 Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
1115 return VERR_INTERNAL_ERROR;
1116 }
1117 memcpy(((uint8_t*)&pState->config) + port, data, cb);
1118 return VINF_SUCCESS;
1119}
1120
1121/**
1122 * Hardware reset. Revert all registers to initial values.
1123 *
1124 * @param pState The device state structure.
1125 */
1126PDMBOTHCBDECL(void) vnetReset(void *pvState)
1127{
1128 VNETSTATE *pState = (VNETSTATE*)pvState;
1129 Log(("%s Reset triggered\n", INSTANCE(pState)));
1130 vpciReset(&pState->VPCI);
1131 // TODO: Implement reset
1132}
1133
1134#ifdef IN_RING3
1135/**
1136 * Wakeup the RX thread.
1137 */
1138static void vnetWakeupReceive(PPDMDEVINS pDevIns)
1139{
1140 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
1141 if ( pState->fMaybeOutOfSpace
1142 && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1143 {
1144 STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
1145 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
1146 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1147 }
1148}
1149
1150/**
1151 * Link Up Timer handler.
1152 *
1153 * @param pDevIns Pointer to device instance structure.
1154 * @param pTimer Pointer to the timer.
1155 * @param pvUser NULL.
1156 * @thread EMT
1157 */
1158static DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1159{
1160 VNETSTATE *pState = (VNETSTATE *)pvUser;
1161
1162 STATUS |= VNET_S_LINK_UP;
1163 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1164}
1165
1166
1167
1168
1169/**
1170 * Handler for the wakeup signaller queue.
1171 */
1172static DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
1173{
1174 vnetWakeupReceive(pDevIns);
1175 return true;
1176}
1177
1178#endif /* IN_RING3 */
1179
1180
1181#ifdef IN_RING3
1182
1183/**
1184 * Check if the device can receive data now.
1185 * This must be called before the pfnRecieve() method is called.
1186 *
1187 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1188 * @param pInterface Pointer to the interface structure containing the called function pointer.
1189 * @thread EMT
1190 */
1191static int vnetCanReceive(VNETSTATE *pState)
1192{
1193 LogFlow(("%s vnetCanReceive\n", INSTANCE(pState)));
1194 int rc = (vqueueIsReady(&pState->VPCI, pState->pRxQueue)
1195 && !vqueueIsEmpty(&pState->VPCI, pState->pRxQueue))
1196 ? VINF_SUCCESS : VERR_NET_NO_BUFFER_SPACE;
1197 LogFlow(("%s vnetCanReceive -> %d\n", INSTANCE(pState), rc));
1198 return rc;
1199}
1200
1201static DECLCALLBACK(int) vnetWaitReceiveAvail(PPDMINETWORKPORT pInterface, unsigned cMillies)
1202{
1203 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
1204 LogFlow(("%s vnetWaitReceiveAvail(cMillies=%u)\n", INSTANCE(pState), cMillies));
1205 int rc = vnetCanReceive(pState);
1206
1207 if (RT_SUCCESS(rc))
1208 return VINF_SUCCESS;
1209 if (RT_UNLIKELY(cMillies == 0))
1210 return VERR_NET_NO_BUFFER_SPACE;
1211
1212 rc = VERR_INTERRUPTED;
1213 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
1214 STAM_PROFILE_START(&pState->StatRxOverflow, a);
1215 while (RT_LIKELY(PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns)) == VMSTATE_RUNNING))
1216 {
1217 int rc2 = vnetCanReceive(pState);
1218 if (RT_SUCCESS(rc2))
1219 {
1220 rc = VINF_SUCCESS;
1221 break;
1222 }
1223 Log(("%s vnetWaitReceiveAvail: waiting cMillies=%u...\n",
1224 INSTANCE(pState), cMillies));
1225 RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
1226 }
1227 STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
1228 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
1229
1230 LogFlow(("%s vnetWaitReceiveAvail -> %d\n", INSTANCE(pState), rc));
1231 return rc;
1232}
1233
1234
1235/**
1236 * Provides interfaces to the driver.
1237 *
1238 * @returns Pointer to interface. NULL if the interface is not supported.
1239 * @param pInterface Pointer to this interface structure.
1240 * @param enmInterface The requested interface identification.
1241 * @thread EMT
1242 */
1243static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
1244{
1245 VNETSTATE *pState = IFACE_TO_STATE(pInterface, VPCI.IBase);
1246 Assert(&pState->VPCI.IBase == pInterface);
1247 switch (enmInterface)
1248 {
1249 case PDMINTERFACE_NETWORK_PORT:
1250 return &pState->INetworkPort;
1251 case PDMINTERFACE_NETWORK_CONFIG:
1252 return &pState->INetworkConfig;
1253 default:
1254 return vpciQueryInterface(pInterface, enmInterface);
1255 }
1256}
1257
1258/**
1259 * Determines if the packet is to be delivered to upper layer. The following
1260 * filters supported:
1261 * - Exact Unicast/Multicast
1262 * - Promiscuous Unicast/Multicast
1263 * - Multicast
1264 * - VLAN
1265 *
1266 * @returns true if packet is intended for this node.
1267 * @param pState Pointer to the state structure.
1268 * @param pvBuf The ethernet packet.
1269 * @param cb Number of bytes available in the packet.
1270 */
1271static bool vnetAddressFilter(PVNETSTATE pState, const void *pvBuf, size_t cb)
1272{
1273 return true; // TODO: Implement!
1274}
1275
1276/**
1277 * Pad and store received packet.
1278 *
1279 * @remarks Make sure that the packet appears to upper layer as one coming
1280 * from real Ethernet: pad it and insert FCS.
1281 *
1282 * @returns VBox status code.
1283 * @param pState The device state structure.
1284 * @param pvBuf The available data.
1285 * @param cb Number of bytes available in the buffer.
1286 */
1287static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb)
1288{
1289 VNETHDR hdr;
1290
1291 hdr.u8Flags = 0;
1292 hdr.u8GSOType = VNETHDR_GSO_NONE;
1293
1294 unsigned int uOffset = 0;
1295 for (unsigned int nElem = 0; uOffset < cb; nElem++)
1296 {
1297 VQUEUEELEM elem;
1298 unsigned int nSeg = 0, uElemSize = 0;
1299
1300 if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
1301 {
1302 Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
1303 return VERR_INTERNAL_ERROR;
1304 }
1305
1306 if (elem.nIn < 1)
1307 {
1308 Log(("%s vnetHandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pState)));
1309 return VERR_INTERNAL_ERROR;
1310 }
1311
1312 if (nElem == 0)
1313 {
1314 /* The very first segment of the very first element gets the header. */
1315 if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
1316 {
1317 Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
1318 return VERR_INTERNAL_ERROR;
1319 }
1320
1321 elem.aSegsIn[nSeg++].pv = &hdr;
1322 uElemSize += sizeof(VNETHDR);
1323 }
1324
1325 while (nSeg < elem.nIn && uOffset < cb)
1326 {
1327 unsigned int uSize = RT_MIN(elem.aSegsIn[nSeg].cb, cb - uOffset);
1328 elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
1329 uOffset += uSize;
1330 uElemSize += uSize;
1331 }
1332 vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize);
1333 }
1334 vqueueSync(&pState->VPCI, pState->pRxQueue);
1335
1336 return VINF_SUCCESS;
1337}
1338
1339/**
1340 * Receive data from the network.
1341 *
1342 * @returns VBox status code.
1343 * @param pInterface Pointer to the interface structure containing the called function pointer.
1344 * @param pvBuf The available data.
1345 * @param cb Number of bytes available in the buffer.
1346 * @thread ???
1347 */
1348static DECLCALLBACK(int) vnetReceive(PPDMINETWORKPORT pInterface, const void *pvBuf, size_t cb)
1349{
1350 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
1351 int rc = VINF_SUCCESS;
1352
1353 Log2(("%s vnetReceive: pvBuf=%p cb=%u\n", INSTANCE(pState), pvBuf, cb));
1354 rc = vnetCanReceive(pState);
1355 if (RT_FAILURE(rc))
1356 return rc;
1357
1358 if (vnetAddressFilter(pState, pvBuf, cb))
1359 rc = vnetHandleRxPacket(pState, pvBuf, cb);
1360
1361 return rc;
1362}
1363
1364/**
1365 * Gets the current Media Access Control (MAC) address.
1366 *
1367 * @returns VBox status code.
1368 * @param pInterface Pointer to the interface structure containing the called function pointer.
1369 * @param pMac Where to store the MAC address.
1370 * @thread EMT
1371 */
1372static DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
1373{
1374 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1375 memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
1376 return VINF_SUCCESS;
1377}
1378
1379/**
1380 * Gets the new link state.
1381 *
1382 * @returns The current link state.
1383 * @param pInterface Pointer to the interface structure containing the called function pointer.
1384 * @thread EMT
1385 */
1386static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
1387{
1388 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1389 if (STATUS & VNET_S_LINK_UP)
1390 return PDMNETWORKLINKSTATE_UP;
1391 return PDMNETWORKLINKSTATE_DOWN;
1392}
1393
1394
1395/**
1396 * Sets the new link state.
1397 *
1398 * @returns VBox status code.
1399 * @param pInterface Pointer to the interface structure containing the called function pointer.
1400 * @param enmState The new link state
1401 */
1402static DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
1403{
1404 VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1405 bool fOldUp = !!(STATUS & VNET_S_LINK_UP);
1406 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
1407
1408 if (fNewUp != fOldUp)
1409 {
1410 if (fNewUp)
1411 {
1412 Log(("%s Link is up\n", INSTANCE(pState)));
1413 STATUS |= VNET_S_LINK_UP;
1414 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1415 }
1416 else
1417 {
1418 Log(("%s Link is down\n", INSTANCE(pState)));
1419 STATUS &= ~VNET_S_LINK_UP;
1420 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1421 }
1422 if (pState->pDrv)
1423 pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
1424 }
1425 return VINF_SUCCESS;
1426}
1427
1428static DECLCALLBACK(void) vnetQueueReceive(void *pvState, PVQUEUE pQueue)
1429{
1430 VNETSTATE *pState = (VNETSTATE*)pvState;
1431 Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pState)));
1432 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
1433}
1434
1435static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1436{
1437 VNETSTATE *pState = (VNETSTATE*)pvState;
1438 if ((pState->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
1439 {
1440 Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n",
1441 INSTANCE(pState), pState->VPCI.uStatus));
1442 return;
1443 }
1444
1445 VQUEUEELEM elem;
1446 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1447 {
1448 unsigned int uOffset = 0;
1449 if (elem.nOut < 2 || elem.aSegsOut[0].cb != sizeof(VNETHDR))
1450 {
1451 Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
1452 INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, sizeof(VNETHDR)));
1453 vqueueElemFree(&elem);
1454 break; /* For now we simply ignore the header, but it must be there anyway! */
1455 }
1456 else
1457 {
1458 uint8_t *pFrame = (uint8_t *)RTMemAllocZ(VNET_MAX_FRAME_SIZE);
1459 if (!pFrame)
1460 {
1461 Log(("%s vnetQueueTransmit: Failed to allocate %u bytes.\n",
1462 INSTANCE(pState), VNET_MAX_FRAME_SIZE));
1463 vqueueElemFree(&elem);
1464 break; /* For now we simply ignore the header, but it must be there anyway! */
1465 }
1466
1467 /* Assemble a complete frame. */
1468 for (unsigned int i = 1; i < elem.nOut && uOffset < VNET_MAX_FRAME_SIZE; i++)
1469 {
1470 unsigned int uSize = elem.aSegsOut[i].cb;
1471 if (uSize > VNET_MAX_FRAME_SIZE - uOffset)
1472 {
1473 Log(("%s vnetQueueTransmit: Packet is too big (>64k), truncating...\n", INSTANCE(pState)));
1474 uSize = VNET_MAX_FRAME_SIZE - uOffset;
1475 }
1476 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1477 pFrame + uOffset, uSize);
1478 uOffset += uSize;
1479 }
1480 STAM_PROFILE_ADV_START(&pState->StatTransmitSend, a);
1481 int rc = pState->pDrv->pfnSend(pState->pDrv, pFrame, uOffset);
1482 STAM_PROFILE_ADV_STOP(&pState->StatTransmitSend, a);
1483 RTMemFree(pFrame);
1484 }
1485 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1486 vqueueSync(&pState->VPCI, pQueue);
1487 }
1488}
1489
1490static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1491{
1492 VNETSTATE *pState = (VNETSTATE*)pvState;
1493 Log(("%s Pending control message\n", INSTANCE(pState)));
1494}
1495
1496
1497/**
1498 * Construct a device instance for a VM.
1499 *
1500 * @returns VBox status.
1501 * @param pDevIns The device instance data.
1502 * If the registration structure is needed, pDevIns->pDevReg points to it.
1503 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1504 * The device number is also found in pDevIns->iInstance, but since it's
1505 * likely to be freqently used PDM passes it as parameter.
1506 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1507 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1508 * iInstance it's expected to be used a bit in this function.
1509 * @thread EMT
1510 */
1511static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1512{
1513 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1514 int rc;
1515
1516 /* Initialize PCI part first. */
1517 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1518 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance, VIRTIO_NET_ID, sizeof(VNetPCIConfig));
1519 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive);
1520 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit);
1521 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl);
1522
1523 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1524
1525 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1526
1527 /*
1528 * Validate configuration.
1529 */
1530 if (!CFGMR3AreValuesValid(pCfgHandle, "MAC\0" "CableConnected\0" "LineSpeed\0"))
1531 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1532 N_("Invalid configuraton for VirtioNet device"));
1533
1534 /* Get config params */
1535 rc = CFGMR3QueryBytes(pCfgHandle, "MAC", pState->config.mac.au8,
1536 sizeof(pState->config.mac.au8));
1537 if (RT_FAILURE(rc))
1538 return PDMDEV_SET_ERROR(pDevIns, rc,
1539 N_("Configuration error: Failed to get MAC address"));
1540 rc = CFGMR3QueryBool(pCfgHandle, "CableConnected", &pState->fCableConnected);
1541 if (RT_FAILURE(rc))
1542 return PDMDEV_SET_ERROR(pDevIns, rc,
1543 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1544
1545 /* Initialize state structure */
1546 pState->fLocked = false;
1547 pState->u32PktNo = 1;
1548
1549 /* Interfaces */
1550 pState->INetworkPort.pfnWaitReceiveAvail = vnetWaitReceiveAvail;
1551 pState->INetworkPort.pfnReceive = vnetReceive;
1552 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1553 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1554 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1555
1556 /* Register save/restore state handlers. */
1557 // TODO:
1558 /*
1559 rc = PDMDevHlpSSMRegisterEx(pDevIns, VVNET_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
1560 NULL, NULL, NULL,
1561 NULL, vnetSaveExec, NULL,
1562 NULL, vnetLoadExec, vnetLoadDone);
1563 if (RT_FAILURE(rc))
1564 return rc;*/
1565
1566
1567 /* Create Link Up Timer */
1568 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
1569 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
1570 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
1571 if (RT_FAILURE(rc))
1572 return rc;
1573
1574 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1575 if (RT_SUCCESS(rc))
1576 {
1577 if (rc == VINF_NAT_DNS)
1578 {
1579 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1580 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"));
1581 }
1582 pState->pDrv = (PPDMINETWORKCONNECTOR)
1583 pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
1584 if (!pState->pDrv)
1585 {
1586 AssertMsgFailed(("%s Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
1587 return VERR_PDM_MISSING_INTERFACE_BELOW;
1588 }
1589 }
1590 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1591 {
1592 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
1593 }
1594 else
1595 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
1596
1597 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
1598 if (RT_FAILURE(rc))
1599 return rc;
1600
1601 vnetReset(pState);
1602
1603 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
1604 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
1605#if defined(VBOX_WITH_STATISTICS)
1606 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
1607 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
1608 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
1609 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
1610 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
1611#endif /* VBOX_WITH_STATISTICS */
1612
1613 return VINF_SUCCESS;
1614}
1615
1616/**
1617 * Destruct a device instance.
1618 *
1619 * We need to free non-VM resources only.
1620 *
1621 * @returns VBox status.
1622 * @param pDevIns The device instance data.
1623 * @thread EMT
1624 */
1625static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1626{
1627 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1628
1629 Log(("%s Destroying instance\n", INSTANCE(pState)));
1630 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1631 {
1632 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1633 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1634 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1635 }
1636
1637 return vpciDestruct(&pState->VPCI);
1638}
1639
1640/**
1641 * Device relocation callback.
1642 *
1643 * When this callback is called the device instance data, and if the
1644 * device have a GC component, is being relocated, or/and the selectors
1645 * have been changed. The device must use the chance to perform the
1646 * necessary pointer relocations and data updates.
1647 *
1648 * Before the GC code is executed the first time, this function will be
1649 * called with a 0 delta so GC pointer calculations can be one in one place.
1650 *
1651 * @param pDevIns Pointer to the device instance.
1652 * @param offDelta The relocation delta relative to the old location.
1653 *
1654 * @remark A relocation CANNOT fail.
1655 */
1656static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1657{
1658 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1659 vpciRelocate(pDevIns, offDelta);
1660 // TBD
1661}
1662
1663/**
1664 * @copydoc FNPDMDEVSUSPEND
1665 */
1666static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1667{
1668 /* Poke thread waiting for buffer space. */
1669 vnetWakeupReceive(pDevIns);
1670}
1671
1672
1673#ifdef VBOX_DYNAMIC_NET_ATTACH
1674/**
1675 * Detach notification.
1676 *
1677 * One port on the network card has been disconnected from the network.
1678 *
1679 * @param pDevIns The device instance.
1680 * @param iLUN The logical unit which is being detached.
1681 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1682 */
1683static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1684{
1685 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1686 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1687
1688 AssertLogRelReturnVoid(iLUN == 0);
1689
1690 vpciCsEnter(&pState->VPCI, VERR_SEM_BUSY);
1691
1692 /*
1693 * Zero some important members.
1694 */
1695 pState->pDrvBase = NULL;
1696 pState->pDrv = NULL;
1697
1698 vpciCsLeave(&pState->VPCI);
1699}
1700
1701
1702/**
1703 * Attach the Network attachment.
1704 *
1705 * One port on the network card has been connected to a network.
1706 *
1707 * @returns VBox status code.
1708 * @param pDevIns The device instance.
1709 * @param iLUN The logical unit which is being attached.
1710 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1711 *
1712 * @remarks This code path is not used during construction.
1713 */
1714static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1715{
1716 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1717 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1718
1719 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1720
1721 vpciCsEnter(&pState->VPCI, VERR_SEM_BUSY);
1722
1723 /*
1724 * Attach the driver.
1725 */
1726 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1727 if (RT_SUCCESS(rc))
1728 {
1729 if (rc == VINF_NAT_DNS)
1730 {
1731#ifdef RT_OS_LINUX
1732 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1733 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"));
1734#else
1735 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1736 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"));
1737#endif
1738 }
1739 pState->pDrv = (PPDMINETWORKCONNECTOR)pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
1740 if (!pState->pDrv)
1741 {
1742 AssertMsgFailed(("Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
1743 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
1744 }
1745 }
1746 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1747 Log(("%s No attached driver!\n", INSTANCE(pState)));
1748
1749
1750 /*
1751 * Temporary set the link down if it was up so that the guest
1752 * will know that we have change the configuration of the
1753 * network card
1754 */
1755 if ((STATUS & VNET_S_LINK_UP) && RT_SUCCESS(rc))
1756 {
1757 STATUS &= ~VNET_S_LINK_UP;
1758 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1759 /* Restore the link back in 5 seconds. */
1760 vpciArmTimer(&pState->VPCI, pState->pLinkUpTimer, 5000000);
1761 }
1762
1763 vpciCsLeave(&pState->VPCI);
1764 return rc;
1765
1766}
1767#endif /* VBOX_DYNAMIC_NET_ATTACH */
1768
1769
1770/**
1771 * @copydoc FNPDMDEVPOWEROFF
1772 */
1773static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1774{
1775 /* Poke thread waiting for buffer space. */
1776 vnetWakeupReceive(pDevIns);
1777}
1778
1779/**
1780 * The device registration structure.
1781 */
1782const PDMDEVREG g_DeviceVirtioNet =
1783{
1784 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
1785 PDM_DEVREG_VERSION,
1786 /* Device name. */
1787 "virtio-net",
1788 /* Name of guest context module (no path).
1789 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1790 "VBoxDDGC.gc",
1791 /* Name of ring-0 module (no path).
1792 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
1793 "VBoxDDR0.r0",
1794 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
1795 * remain unchanged from registration till VM destruction. */
1796 "Virtio Ethernet.\n",
1797
1798 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
1799 PDM_DEVREG_FLAGS_DEFAULT_BITS, // | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1800 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
1801 PDM_DEVREG_CLASS_NETWORK,
1802 /* Maximum number of instances (per VM). */
1803 8,
1804 /* Size of the instance data. */
1805 sizeof(VNETSTATE),
1806
1807 /* Construct instance - required. */
1808 vnetConstruct,
1809 /* Destruct instance - optional. */
1810 vnetDestruct,
1811 /* Relocation command - optional. */
1812 vnetRelocate,
1813 /* I/O Control interface - optional. */
1814 NULL,
1815 /* Power on notification - optional. */
1816 NULL,
1817 /* Reset notification - optional. */
1818 NULL,
1819 /* Suspend notification - optional. */
1820 vnetSuspend,
1821 /* Resume notification - optional. */
1822 NULL,
1823#ifdef VBOX_DYNAMIC_NET_ATTACH
1824 /* Attach command - optional. */
1825 vnetAttach,
1826 /* Detach notification - optional. */
1827 vnetDetach,
1828#else /* !VBOX_DYNAMIC_NET_ATTACH */
1829 /* Attach command - optional. */
1830 NULL,
1831 /* Detach notification - optional. */
1832 NULL,
1833#endif /* !VBOX_DYNAMIC_NET_ATTACH */
1834 /* Query a LUN base interface - optional. */
1835 NULL,
1836 /* Init complete notification - optional. */
1837 NULL,
1838 /* Power off notification - optional. */
1839 vnetPowerOff,
1840 /* pfnSoftReset */
1841 NULL,
1842 /* u32VersionEnd */
1843 PDM_DEVREG_VERSION
1844};
1845
1846#endif /* IN_RING3 */
1847#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1848
Note: See TracBrowser for help on using the repository browser.

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