VirtualBox

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

Last change on this file since 23913 was 23830, checked in by vboxsync, 16 years ago

#3987: Virtio: Reset, feature negotiation, notification control.

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