VirtualBox

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

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

#3987: Virtio: Queues and packet reception/transmission.

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