VirtualBox

source: vbox/trunk/src/VBox/Devices/VirtIO/Virtio.cpp@ 37738

Last change on this file since 37738 was 37466, checked in by vboxsync, 13 years ago

VMM,Devices: Automatically use a per-device lock instead of the giant IOM lock. With exception of the PIC, APIC, IOAPIC and PCI buses which are all using the PDM crit sect, there should be no calls between devices. So, this change should be relatively safe.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/* $Id: Virtio.cpp 37466 2011-06-15 12:44:16Z vboxsync $ */
2/** @file
3 * Virtio - Virtio Common Functions (VRing, VQueue, Virtio PCI)
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
20
21#include <iprt/param.h>
22#include <iprt/uuid.h>
23#include <VBox/vmm/pdmdev.h>
24#include "Virtio.h"
25
26#define INSTANCE(pState) pState->szInstance
27#define IFACE_TO_STATE(pIface, ifaceName) ((VPCISTATE *)((char*)pIface - RT_OFFSETOF(VPCISTATE, ifaceName)))
28
29#ifdef LOG_ENABLED
30#define QUEUENAME(s, q) (q->pcszName)
31#endif /* DEBUG */
32
33
34
35#ifndef VBOX_DEVICE_STRUCT_TESTCASE
36
37//RT_C_DECLS_BEGIN
38//RT_C_DECLS_END
39
40
41static void vqueueReset(PVQUEUE pQueue)
42{
43 pQueue->VRing.addrDescriptors = 0;
44 pQueue->VRing.addrAvail = 0;
45 pQueue->VRing.addrUsed = 0;
46 pQueue->uNextAvailIndex = 0;
47 pQueue->uNextUsedIndex = 0;
48 pQueue->uPageNumber = 0;
49}
50
51static void vqueueInit(PVQUEUE pQueue, uint32_t uPageNumber)
52{
53 pQueue->VRing.addrDescriptors = (uint64_t)uPageNumber << PAGE_SHIFT;
54 pQueue->VRing.addrAvail = pQueue->VRing.addrDescriptors
55 + sizeof(VRINGDESC) * pQueue->VRing.uSize;
56 pQueue->VRing.addrUsed = RT_ALIGN(
57 pQueue->VRing.addrAvail + RT_OFFSETOF(VRINGAVAIL, auRing[pQueue->VRing.uSize]),
58 PAGE_SIZE); /* The used ring must start from the next page. */
59 pQueue->uNextAvailIndex = 0;
60 pQueue->uNextUsedIndex = 0;
61}
62
63// void vqueueElemFree(PVQUEUEELEM pElem)
64// {
65// }
66
67void vringReadDesc(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex, PVRINGDESC pDesc)
68{
69 //Log(("%s vringReadDesc: ring=%p idx=%u\n", INSTANCE(pState), pVRing, uIndex));
70 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
71 pVRing->addrDescriptors + sizeof(VRINGDESC) * (uIndex % pVRing->uSize),
72 pDesc, sizeof(VRINGDESC));
73}
74
75uint16_t vringReadAvail(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex)
76{
77 uint16_t tmp;
78
79 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
80 pVRing->addrAvail + RT_OFFSETOF(VRINGAVAIL, auRing[uIndex % pVRing->uSize]),
81 &tmp, sizeof(tmp));
82 return tmp;
83}
84
85uint16_t vringReadAvailFlags(PVPCISTATE pState, PVRING pVRing)
86{
87 uint16_t tmp;
88
89 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
90 pVRing->addrAvail + RT_OFFSETOF(VRINGAVAIL, uFlags),
91 &tmp, sizeof(tmp));
92 return tmp;
93}
94
95void vringSetNotification(PVPCISTATE pState, PVRING pVRing, bool fEnabled)
96{
97 uint16_t tmp;
98
99 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
100 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uFlags),
101 &tmp, sizeof(tmp));
102
103 if (fEnabled)
104 tmp &= ~ VRINGUSED_F_NO_NOTIFY;
105 else
106 tmp |= VRINGUSED_F_NO_NOTIFY;
107
108 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns),
109 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uFlags),
110 &tmp, sizeof(tmp));
111}
112
113bool vqueueGet(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem)
114{
115 if (vqueueIsEmpty(pState, pQueue))
116 return false;
117
118 pElem->nIn = pElem->nOut = 0;
119
120 Log2(("%s vqueueGet: %s avail_idx=%u\n", INSTANCE(pState),
121 QUEUENAME(pState, pQueue), pQueue->uNextAvailIndex));
122
123 VRINGDESC desc;
124 uint16_t idx = vringReadAvail(pState, &pQueue->VRing, pQueue->uNextAvailIndex++);
125 pElem->uIndex = idx;
126 do
127 {
128 VQUEUESEG *pSeg;
129
130 vringReadDesc(pState, &pQueue->VRing, idx, &desc);
131 if (desc.u16Flags & VRINGDESC_F_WRITE)
132 {
133 Log2(("%s vqueueGet: %s IN seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pState),
134 QUEUENAME(pState, pQueue), pElem->nIn, idx, desc.u64Addr, desc.uLen));
135 pSeg = &pElem->aSegsIn[pElem->nIn++];
136 }
137 else
138 {
139 Log2(("%s vqueueGet: %s OUT seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pState),
140 QUEUENAME(pState, pQueue), pElem->nOut, idx, desc.u64Addr, desc.uLen));
141 pSeg = &pElem->aSegsOut[pElem->nOut++];
142 }
143
144 pSeg->addr = desc.u64Addr;
145 pSeg->cb = desc.uLen;
146 pSeg->pv = NULL;
147
148 idx = desc.u16Next;
149 } while (desc.u16Flags & VRINGDESC_F_NEXT);
150
151 Log2(("%s vqueueGet: %s head_desc_idx=%u nIn=%u nOut=%u\n", INSTANCE(pState),
152 QUEUENAME(pState, pQueue), pElem->uIndex, pElem->nIn, pElem->nOut));
153 return true;
154}
155
156uint16_t vringReadUsedIndex(PVPCISTATE pState, PVRING pVRing)
157{
158 uint16_t tmp;
159 PDMDevHlpPhysRead(pState->CTX_SUFF(pDevIns),
160 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uIndex),
161 &tmp, sizeof(tmp));
162 return tmp;
163}
164
165void vringWriteUsedIndex(PVPCISTATE pState, PVRING pVRing, uint16_t u16Value)
166{
167 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns),
168 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, uIndex),
169 &u16Value, sizeof(u16Value));
170}
171
172void vringWriteUsedElem(PVPCISTATE pState, PVRING pVRing, uint32_t uIndex, uint32_t uId, uint32_t uLen)
173{
174 VRINGUSEDELEM elem;
175
176 elem.uId = uId;
177 elem.uLen = uLen;
178 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns),
179 pVRing->addrUsed + RT_OFFSETOF(VRINGUSED, aRing[uIndex % pVRing->uSize]),
180 &elem, sizeof(elem));
181}
182
183void vqueuePut(PVPCISTATE pState, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen, uint32_t uReserved)
184{
185 unsigned int i, uOffset, cbReserved = uReserved;
186
187 Log2(("%s vqueuePut: %s desc_idx=%u acb=%u\n", INSTANCE(pState),
188 QUEUENAME(pState, pQueue), pElem->uIndex, uLen));
189 for (i = uOffset = 0; i < pElem->nIn && uOffset < uLen - uReserved; i++)
190 {
191 uint32_t cbSegLen = RT_MIN(uLen - cbReserved - uOffset, pElem->aSegsIn[i].cb - cbReserved);
192 if (pElem->aSegsIn[i].pv)
193 {
194 Log2(("%s vqueuePut: %s used_idx=%u seg=%u addr=%p pv=%p cb=%u acb=%u\n", INSTANCE(pState),
195 QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, i, pElem->aSegsIn[i].addr, pElem->aSegsIn[i].pv, pElem->aSegsIn[i].cb, cbSegLen));
196 PDMDevHlpPhysWrite(pState->CTX_SUFF(pDevIns), pElem->aSegsIn[i].addr + cbReserved,
197 pElem->aSegsIn[i].pv, cbSegLen);
198 cbReserved = 0;
199 }
200 uOffset += cbSegLen;
201 }
202
203 Assert((uReserved + uOffset) == uLen || pElem->nIn == 0);
204 Log2(("%s vqueuePut: %s used_idx=%u guest_used_idx=%u id=%u len=%u\n", INSTANCE(pState),
205 QUEUENAME(pState, pQueue), pQueue->uNextUsedIndex, vringReadUsedIndex(pState, &pQueue->VRing), pElem->uIndex, uLen));
206 vringWriteUsedElem(pState, &pQueue->VRing, pQueue->uNextUsedIndex++, pElem->uIndex, uLen);
207}
208
209void vqueueNotify(PVPCISTATE pState, PVQUEUE pQueue)
210{
211 LogFlow(("%s vqueueNotify: %s availFlags=%x guestFeatures=%x vqueue is %sempty\n",
212 INSTANCE(pState), QUEUENAME(pState, pQueue),
213 vringReadAvailFlags(pState, &pQueue->VRing),
214 pState->uGuestFeatures, vqueueIsEmpty(pState, pQueue)?"":"not "));
215 if (!(vringReadAvailFlags(pState, &pQueue->VRing) & VRINGAVAIL_F_NO_INTERRUPT)
216 || ((pState->uGuestFeatures & VPCI_F_NOTIFY_ON_EMPTY) && vqueueIsEmpty(pState, pQueue)))
217 {
218 int rc = vpciRaiseInterrupt(pState, VERR_INTERNAL_ERROR, VPCI_ISR_QUEUE);
219 if (RT_FAILURE(rc))
220 Log(("%s vqueueNotify: Failed to raise an interrupt (%Rrc).\n", INSTANCE(pState), rc));
221 }
222 else
223 {
224 STAM_COUNTER_INC(&pState->StatIntsSkipped);
225 }
226
227}
228
229void vqueueSync(PVPCISTATE pState, PVQUEUE pQueue)
230{
231 Log2(("%s vqueueSync: %s old_used_idx=%u new_used_idx=%u\n", INSTANCE(pState),
232 QUEUENAME(pState, pQueue), vringReadUsedIndex(pState, &pQueue->VRing), pQueue->uNextUsedIndex));
233 vringWriteUsedIndex(pState, &pQueue->VRing, pQueue->uNextUsedIndex);
234 vqueueNotify(pState, pQueue);
235}
236
237void vpciReset(PVPCISTATE pState)
238{
239 pState->uGuestFeatures = 0;
240 pState->uQueueSelector = 0;
241 pState->uStatus = 0;
242 pState->uISR = 0;
243
244 for (unsigned i = 0; i < pState->nQueues; i++)
245 vqueueReset(&pState->Queues[i]);
246}
247
248
249/**
250 * Raise interrupt.
251 *
252 * @param pState The device state structure.
253 * @param rcBusy Status code to return when the critical section is busy.
254 * @param u8IntCause Interrupt cause bit mask to set in PCI ISR port.
255 */
256int vpciRaiseInterrupt(VPCISTATE *pState, int rcBusy, uint8_t u8IntCause)
257{
258 // int rc = vpciCsEnter(pState, rcBusy);
259 // if (RT_UNLIKELY(rc != VINF_SUCCESS))
260 // return rc;
261
262 STAM_COUNTER_INC(&pState->StatIntsRaised);
263 LogFlow(("%s vpciRaiseInterrupt: u8IntCause=%x\n",
264 INSTANCE(pState), u8IntCause));
265
266 pState->uISR |= u8IntCause;
267 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 1);
268 // vpciCsLeave(pState);
269 return VINF_SUCCESS;
270}
271
272/**
273 * Lower interrupt.
274 *
275 * @param pState The device state structure.
276 */
277PDMBOTHCBDECL(void) vpciLowerInterrupt(VPCISTATE *pState)
278{
279 LogFlow(("%s vpciLowerInterrupt\n", INSTANCE(pState)));
280 PDMDevHlpPCISetIrq(pState->CTX_SUFF(pDevIns), 0, 0);
281}
282
283DECLINLINE(uint32_t) vpciGetHostFeatures(PVPCISTATE pState,
284 PFNGETHOSTFEATURES pfnGetHostFeatures)
285{
286 return pfnGetHostFeatures(pState)
287 | VPCI_F_NOTIFY_ON_EMPTY;
288}
289
290/**
291 * Port I/O Handler for IN operations.
292 *
293 * @returns VBox status code.
294 *
295 * @param pDevIns The device instance.
296 * @param pvUser Pointer to the device state structure.
297 * @param port Port number used for the IN operation.
298 * @param pu32 Where to store the result.
299 * @param cb Number of bytes read.
300 * @thread EMT
301 */
302int vpciIOPortIn(PPDMDEVINS pDevIns,
303 void *pvUser,
304 RTIOPORT port,
305 uint32_t *pu32,
306 unsigned cb,
307 PFNGETHOSTFEATURES pfnGetHostFeatures,
308 PFNGETCONFIG pfnGetConfig)
309{
310 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
311 int rc = VINF_SUCCESS;
312 const char *szInst = INSTANCE(pState);
313 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIORead), a);
314
315 /*
316 * We probably do not need to enter critical section when reading registers
317 * as the most of them are either constant or being changed during
318 * initialization only, the exception being ISR which can be raced by all
319 * threads but I see no big harm in it. It also happens to be the most read
320 * register as it gets read in interrupt handler. By dropping cs protection
321 * here we gain the ability to deliver RX packets to the guest while TX is
322 * holding cs transmitting queued packets.
323 *
324 rc = vpciCsEnter(pState, VINF_IOM_HC_IOPORT_READ);
325 if (RT_UNLIKELY(rc != VINF_SUCCESS))
326 {
327 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIORead), a);
328 return rc;
329 }*/
330
331 port -= pState->addrIOPort;
332 switch (port)
333 {
334 case VPCI_HOST_FEATURES:
335 /* Tell the guest what features we support. */
336 *pu32 = vpciGetHostFeatures(pState, pfnGetHostFeatures)
337 | VPCI_F_BAD_FEATURE;
338 break;
339
340 case VPCI_GUEST_FEATURES:
341 *pu32 = pState->uGuestFeatures;
342 break;
343
344 case VPCI_QUEUE_PFN:
345 *pu32 = pState->Queues[pState->uQueueSelector].uPageNumber;
346 break;
347
348 case VPCI_QUEUE_NUM:
349 Assert(cb == 2);
350 *(uint16_t*)pu32 = pState->Queues[pState->uQueueSelector].VRing.uSize;
351 break;
352
353 case VPCI_QUEUE_SEL:
354 Assert(cb == 2);
355 *(uint16_t*)pu32 = pState->uQueueSelector;
356 break;
357
358 case VPCI_STATUS:
359 Assert(cb == 1);
360 *(uint8_t*)pu32 = pState->uStatus;
361 break;
362
363 case VPCI_ISR:
364 Assert(cb == 1);
365 *(uint8_t*)pu32 = pState->uISR;
366 pState->uISR = 0; /* read clears all interrupts */
367 vpciLowerInterrupt(pState);
368 break;
369
370 default:
371 if (port >= VPCI_CONFIG)
372 {
373 rc = pfnGetConfig(pState, port - VPCI_CONFIG, cb, pu32);
374 }
375 else
376 {
377 *pu32 = 0xFFFFFFFF;
378 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortIn: "
379 "no valid port at offset port=%RTiop "
380 "cb=%08x\n", szInst, port, cb);
381 }
382 break;
383 }
384 Log3(("%s vpciIOPortIn: At %RTiop in %0*x\n",
385 szInst, port, cb*2, *pu32));
386 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIORead), a);
387 //vpciCsLeave(pState);
388 return rc;
389}
390
391
392/**
393 * Port I/O Handler for OUT operations.
394 *
395 * @returns VBox status code.
396 *
397 * @param pDevIns The device instance.
398 * @param pvUser User argument.
399 * @param Port Port number used for the IN operation.
400 * @param u32 The value to output.
401 * @param cb The value size in bytes.
402 * @thread EMT
403 */
404int vpciIOPortOut(PPDMDEVINS pDevIns,
405 void *pvUser,
406 RTIOPORT port,
407 uint32_t u32,
408 unsigned cb,
409 PFNGETHOSTMINIMALFEATURES pfnGetHostMinimalFeatures,
410 PFNGETHOSTFEATURES pfnGetHostFeatures,
411 PFNSETHOSTFEATURES pfnSetHostFeatures,
412 PFNRESET pfnReset,
413 PFNREADY pfnReady,
414 PFNSETCONFIG pfnSetConfig)
415
416{
417 VPCISTATE *pState = PDMINS_2_DATA(pDevIns, VPCISTATE *);
418 int rc = VINF_SUCCESS;
419 const char *szInst = INSTANCE(pState);
420 bool fHasBecomeReady;
421 STAM_PROFILE_ADV_START(&pState->CTXSUFF(StatIOWrite), a);
422
423 port -= pState->addrIOPort;
424 Log3(("%s virtioIOPortOut: At %RTiop out %0*x\n", szInst, port, cb*2, u32));
425
426 switch (port)
427 {
428 case VPCI_GUEST_FEATURES:
429 /* Check if the guest negotiates properly, fall back to basics if it does not. */
430 if (VPCI_F_BAD_FEATURE & u32)
431 {
432 Log(("%s WARNING! Guest failed to negotiate properly (guest=%x)\n",
433 INSTANCE(pState), u32));
434 pState->uGuestFeatures = pfnGetHostMinimalFeatures(pState);
435 }
436 /* The guest may potentially desire features we don't support! */
437 else if (~vpciGetHostFeatures(pState, pfnGetHostFeatures) & u32)
438 {
439 Log(("%s Guest asked for features host does not support! (host=%x guest=%x)\n",
440 INSTANCE(pState),
441 vpciGetHostFeatures(pState, pfnGetHostFeatures), u32));
442 pState->uGuestFeatures =
443 vpciGetHostFeatures(pState, pfnGetHostFeatures);
444 }
445 else
446 pState->uGuestFeatures = u32;
447 pfnSetHostFeatures(pState, pState->uGuestFeatures);
448 break;
449
450 case VPCI_QUEUE_PFN:
451 /*
452 * The guest is responsible for allocating the pages for queues,
453 * here it provides us with the page number of descriptor table.
454 * Note that we provide the size of the queue to the guest via
455 * VIRTIO_PCI_QUEUE_NUM.
456 */
457 pState->Queues[pState->uQueueSelector].uPageNumber = u32;
458 if (u32)
459 vqueueInit(&pState->Queues[pState->uQueueSelector], u32);
460 else
461 rc = pfnReset(pState);
462 break;
463
464 case VPCI_QUEUE_SEL:
465 Assert(cb == 2);
466 u32 &= 0xFFFF;
467 if (u32 < pState->nQueues)
468 pState->uQueueSelector = u32;
469 else
470 Log3(("%s vpciIOPortOut: Invalid queue selector %08x\n", szInst, u32));
471 break;
472
473 case VPCI_QUEUE_NOTIFY:
474#ifdef IN_RING3
475 Assert(cb == 2);
476 u32 &= 0xFFFF;
477 if (u32 < pState->nQueues)
478 if (pState->Queues[u32].VRing.addrDescriptors)
479 {
480 // rc = vpciCsEnter(pState, VERR_SEM_BUSY);
481 // if (RT_LIKELY(rc == VINF_SUCCESS))
482 // {
483 pState->Queues[u32].pfnCallback(pState, &pState->Queues[u32]);
484 // vpciCsLeave(pState);
485 // }
486 }
487 else
488 Log(("%s The queue (#%d) being notified has not been initialized.\n",
489 INSTANCE(pState), u32));
490 else
491 Log(("%s Invalid queue number (%d)\n", INSTANCE(pState), u32));
492#else
493 rc = VINF_IOM_HC_IOPORT_WRITE;
494#endif
495 break;
496
497 case VPCI_STATUS:
498 Assert(cb == 1);
499 u32 &= 0xFF;
500 fHasBecomeReady = !(pState->uStatus & VPCI_STATUS_DRV_OK) && (u32 & VPCI_STATUS_DRV_OK);
501 pState->uStatus = u32;
502 /* Writing 0 to the status port triggers device reset. */
503 if (u32 == 0)
504 rc = pfnReset(pState);
505 else if (fHasBecomeReady)
506 pfnReady(pState);
507 break;
508
509 default:
510 if (port >= VPCI_CONFIG)
511 rc = pfnSetConfig(pState, port - VPCI_CONFIG, cb, &u32);
512 else
513 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortOut: no valid port at offset port=%RTiop cb=%08x\n", szInst, port, cb);
514 break;
515 }
516
517 STAM_PROFILE_ADV_STOP(&pState->CTXSUFF(StatIOWrite), a);
518 return rc;
519}
520
521#ifdef IN_RING3
522
523/**
524 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
525 */
526void *vpciQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
527{
528 VPCISTATE *pThis = IFACE_TO_STATE(pInterface, IBase);
529 Assert(&pThis->IBase == pInterface);
530
531 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
532 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
533 return NULL;
534}
535
536/**
537 * Gets the pointer to the status LED of a unit.
538 *
539 * @returns VBox status code.
540 * @param pInterface Pointer to the interface structure.
541 * @param iLUN The unit which status LED we desire.
542 * @param ppLed Where to store the LED pointer.
543 * @thread EMT
544 */
545static DECLCALLBACK(int) vpciQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
546{
547 VPCISTATE *pState = IFACE_TO_STATE(pInterface, ILeds);
548 int rc = VERR_PDM_LUN_NOT_FOUND;
549
550 if (iLUN == 0)
551 {
552 *ppLed = &pState->led;
553 rc = VINF_SUCCESS;
554 }
555 return rc;
556}
557
558/**
559 * Turns on/off the write status LED.
560 *
561 * @returns VBox status code.
562 * @param pState Pointer to the device state structure.
563 * @param fOn New LED state.
564 */
565void vpciSetWriteLed(PVPCISTATE pState, bool fOn)
566{
567 LogFlow(("%s vpciSetWriteLed: %s\n", INSTANCE(pState), fOn?"on":"off"));
568 if (fOn)
569 pState->led.Asserted.s.fWriting = pState->led.Actual.s.fWriting = 1;
570 else
571 pState->led.Actual.s.fWriting = fOn;
572}
573
574/**
575 * Turns on/off the read status LED.
576 *
577 * @returns VBox status code.
578 * @param pState Pointer to the device state structure.
579 * @param fOn New LED state.
580 */
581void vpciSetReadLed(PVPCISTATE pState, bool fOn)
582{
583 LogFlow(("%s vpciSetReadLed: %s\n", INSTANCE(pState), fOn?"on":"off"));
584 if (fOn)
585 pState->led.Asserted.s.fReading = pState->led.Actual.s.fReading = 1;
586 else
587 pState->led.Actual.s.fReading = fOn;
588}
589
590/**
591 * Sets 8-bit register in PCI configuration space.
592 * @param refPciDev The PCI device.
593 * @param uOffset The register offset.
594 * @param u16Value The value to store in the register.
595 * @thread EMT
596 */
597DECLINLINE(void) vpciCfgSetU8(PCIDEVICE& refPciDev, uint32_t uOffset, uint8_t u8Value)
598{
599 Assert(uOffset < sizeof(refPciDev.config));
600 refPciDev.config[uOffset] = u8Value;
601}
602
603/**
604 * Sets 16-bit register in PCI configuration space.
605 * @param refPciDev The PCI device.
606 * @param uOffset The register offset.
607 * @param u16Value The value to store in the register.
608 * @thread EMT
609 */
610DECLINLINE(void) vpciCfgSetU16(PCIDEVICE& refPciDev, uint32_t uOffset, uint16_t u16Value)
611{
612 Assert(uOffset+sizeof(u16Value) <= sizeof(refPciDev.config));
613 *(uint16_t*)&refPciDev.config[uOffset] = u16Value;
614}
615
616/**
617 * Sets 32-bit register in PCI configuration space.
618 * @param refPciDev The PCI device.
619 * @param uOffset The register offset.
620 * @param u32Value The value to store in the register.
621 * @thread EMT
622 */
623DECLINLINE(void) vpciCfgSetU32(PCIDEVICE& refPciDev, uint32_t uOffset, uint32_t u32Value)
624{
625 Assert(uOffset+sizeof(u32Value) <= sizeof(refPciDev.config));
626 *(uint32_t*)&refPciDev.config[uOffset] = u32Value;
627}
628
629
630#ifdef DEBUG
631static void vpciDumpState(PVPCISTATE pState, const char *pcszCaller)
632{
633 Log2(("vpciDumpState: (called from %s)\n"
634 " uGuestFeatures = 0x%08x\n"
635 " uQueueSelector = 0x%04x\n"
636 " uStatus = 0x%02x\n"
637 " uISR = 0x%02x\n",
638 pcszCaller,
639 pState->uGuestFeatures,
640 pState->uQueueSelector,
641 pState->uStatus,
642 pState->uISR));
643
644 for (unsigned i = 0; i < pState->nQueues; i++)
645 Log2((" %s queue:\n"
646 " VRing.uSize = %u\n"
647 " VRing.addrDescriptors = %p\n"
648 " VRing.addrAvail = %p\n"
649 " VRing.addrUsed = %p\n"
650 " uNextAvailIndex = %u\n"
651 " uNextUsedIndex = %u\n"
652 " uPageNumber = %x\n",
653 pState->Queues[i].pcszName,
654 pState->Queues[i].VRing.uSize,
655 pState->Queues[i].VRing.addrDescriptors,
656 pState->Queues[i].VRing.addrAvail,
657 pState->Queues[i].VRing.addrUsed,
658 pState->Queues[i].uNextAvailIndex,
659 pState->Queues[i].uNextUsedIndex,
660 pState->Queues[i].uPageNumber));
661}
662#else
663# define vpciDumpState(x, s) do {} while (0)
664#endif
665
666/**
667 * Saves the state of device.
668 *
669 * @returns VBox status code.
670 * @param pDevIns The device instance.
671 * @param pSSM The handle to the saved state.
672 */
673int vpciSaveExec(PVPCISTATE pState, PSSMHANDLE pSSM)
674{
675 int rc;
676
677 vpciDumpState(pState, "vpciSaveExec");
678
679 rc = SSMR3PutU32(pSSM, pState->uGuestFeatures);
680 AssertRCReturn(rc, rc);
681 rc = SSMR3PutU16(pSSM, pState->uQueueSelector);
682 AssertRCReturn(rc, rc);
683 rc = SSMR3PutU8( pSSM, pState->uStatus);
684 AssertRCReturn(rc, rc);
685 rc = SSMR3PutU8( pSSM, pState->uISR);
686 AssertRCReturn(rc, rc);
687
688 /* Save queue states */
689 rc = SSMR3PutU32(pSSM, pState->nQueues);
690 AssertRCReturn(rc, rc);
691 for (unsigned i = 0; i < pState->nQueues; i++)
692 {
693 rc = SSMR3PutU16(pSSM, pState->Queues[i].VRing.uSize);
694 AssertRCReturn(rc, rc);
695 rc = SSMR3PutU32(pSSM, pState->Queues[i].uPageNumber);
696 AssertRCReturn(rc, rc);
697 rc = SSMR3PutU16(pSSM, pState->Queues[i].uNextAvailIndex);
698 AssertRCReturn(rc, rc);
699 rc = SSMR3PutU16(pSSM, pState->Queues[i].uNextUsedIndex);
700 AssertRCReturn(rc, rc);
701 }
702
703 return VINF_SUCCESS;
704}
705
706/**
707 * Loads a saved device state.
708 *
709 * @returns VBox status code.
710 * @param pDevIns The device instance.
711 * @param pSSM The handle to the saved state.
712 * @param uVersion The data unit version number.
713 * @param uPass The data pass.
714 */
715int vpciLoadExec(PVPCISTATE pState, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass, uint32_t nQueues)
716{
717 int rc;
718
719 if (uPass == SSM_PASS_FINAL)
720 {
721 /* Restore state data */
722 rc = SSMR3GetU32(pSSM, &pState->uGuestFeatures);
723 AssertRCReturn(rc, rc);
724 rc = SSMR3GetU16(pSSM, &pState->uQueueSelector);
725 AssertRCReturn(rc, rc);
726 rc = SSMR3GetU8( pSSM, &pState->uStatus);
727 AssertRCReturn(rc, rc);
728 rc = SSMR3GetU8( pSSM, &pState->uISR);
729 AssertRCReturn(rc, rc);
730
731 /* Restore queues */
732 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
733 {
734 rc = SSMR3GetU32(pSSM, &pState->nQueues);
735 AssertRCReturn(rc, rc);
736 }
737 else
738 pState->nQueues = nQueues;
739 for (unsigned i = 0; i < pState->nQueues; i++)
740 {
741 rc = SSMR3GetU16(pSSM, &pState->Queues[i].VRing.uSize);
742 AssertRCReturn(rc, rc);
743 rc = SSMR3GetU32(pSSM, &pState->Queues[i].uPageNumber);
744 AssertRCReturn(rc, rc);
745
746 if (pState->Queues[i].uPageNumber)
747 vqueueInit(&pState->Queues[i], pState->Queues[i].uPageNumber);
748
749 rc = SSMR3GetU16(pSSM, &pState->Queues[i].uNextAvailIndex);
750 AssertRCReturn(rc, rc);
751 rc = SSMR3GetU16(pSSM, &pState->Queues[i].uNextUsedIndex);
752 AssertRCReturn(rc, rc);
753 }
754 }
755
756 vpciDumpState(pState, "vpciLoadExec");
757
758 return VINF_SUCCESS;
759}
760
761/**
762 * Set PCI configuration space registers.
763 *
764 * @param pci Reference to PCI device structure.
765 * @param uSubsystemId PCI Subsystem Id
766 * @param uClass Class of PCI device (network, etc)
767 * @thread EMT
768 */
769static DECLCALLBACK(void) vpciConfigure(PCIDEVICE& pci,
770 uint16_t uSubsystemId,
771 uint16_t uClass)
772{
773 /* Configure PCI Device, assume 32-bit mode ******************************/
774 PCIDevSetVendorId(&pci, DEVICE_PCI_VENDOR_ID);
775 PCIDevSetDeviceId(&pci, DEVICE_PCI_DEVICE_ID);
776 vpciCfgSetU16(pci, VBOX_PCI_SUBSYSTEM_VENDOR_ID, DEVICE_PCI_SUBSYSTEM_VENDOR_ID);
777 vpciCfgSetU16(pci, VBOX_PCI_SUBSYSTEM_ID, uSubsystemId);
778
779 /* ABI version, must be equal 0 as of 2.6.30 kernel. */
780 vpciCfgSetU8( pci, VBOX_PCI_REVISION_ID, 0x00);
781 /* Ethernet adapter */
782 vpciCfgSetU8( pci, VBOX_PCI_CLASS_PROG, 0x00);
783 vpciCfgSetU16(pci, VBOX_PCI_CLASS_DEVICE, uClass);
784 /* Interrupt Pin: INTA# */
785 vpciCfgSetU8( pci, VBOX_PCI_INTERRUPT_PIN, 0x01);
786
787#ifdef VBOX_WITH_MSI_DEVICES
788 PCIDevSetCapabilityList (&pci, 0x80);
789 PCIDevSetStatus (&pci, VBOX_PCI_STATUS_CAP_LIST);
790#endif
791}
792
793/* WARNING! This function must never be used in multithreaded context! */
794static const char *vpciCounter(const char *pszDevFmt,
795 const char *pszCounter)
796{
797 static char g_szCounterName[80];
798
799 RTStrPrintf(g_szCounterName, sizeof(g_szCounterName),
800 "/Devices/%s/%s", pszDevFmt, pszCounter);
801
802 return g_szCounterName;
803}
804
805// TODO: header
806DECLCALLBACK(int) vpciConstruct(PPDMDEVINS pDevIns, VPCISTATE *pState,
807 int iInstance, const char *pcszNameFmt,
808 uint16_t uSubsystemId, uint16_t uClass,
809 uint32_t nQueues)
810{
811 /* Init handles and log related stuff. */
812 RTStrPrintf(pState->szInstance, sizeof(pState->szInstance),
813 pcszNameFmt, iInstance);
814
815 pState->pDevInsR3 = pDevIns;
816 pState->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
817 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
818 pState->led.u32Magic = PDMLED_MAGIC;
819
820 pState->ILeds.pfnQueryStatusLed = vpciQueryStatusLed;
821
822 /* Initialize critical section. */
823 int rc = PDMDevHlpCritSectInit(pDevIns, &pState->cs, RT_SRC_POS, "%s", pState->szInstance);
824 if (RT_FAILURE(rc))
825 return rc;
826
827 /* Set PCI config registers */
828 vpciConfigure(pState->pciDevice, uSubsystemId, uClass);
829 /* Register PCI device */
830 rc = PDMDevHlpPCIRegister(pDevIns, &pState->pciDevice);
831 if (RT_FAILURE(rc))
832 return rc;
833
834#ifdef VBOX_WITH_MSI_DEVICES
835#if 0
836 {
837 PDMMSIREG aMsiReg;
838
839 RT_ZERO(aMsiReg);
840 aMsiReg.cMsixVectors = 1;
841 aMsiReg.iMsixCapOffset = 0x80;
842 aMsiReg.iMsixNextOffset = 0x0;
843 aMsiReg.iMsixBar = 0;
844 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
845 if (RT_FAILURE (rc))
846 PCIDevSetCapabilityList(&pState->pciDevice, 0x0);
847 }
848#endif
849#endif
850
851 /* Status driver */
852 PPDMIBASE pBase;
853 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pState->IBase, &pBase, "Status Port");
854 if (RT_FAILURE(rc))
855 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
856 pState->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
857
858 pState->nQueues = nQueues;
859
860#if defined(VBOX_WITH_STATISTICS)
861 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in GC", vpciCounter(pcszNameFmt, "IO/ReadGC"), iInstance);
862 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOReadHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in HC", vpciCounter(pcszNameFmt, "IO/ReadHC"), iInstance);
863 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in GC", vpciCounter(pcszNameFmt, "IO/WriteGC"), iInstance);
864 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIOWriteHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in HC", vpciCounter(pcszNameFmt, "IO/WriteHC"), iInstance);
865 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIntsRaised, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of raised interrupts", vpciCounter(pcszNameFmt, "Interrupts/Raised"), iInstance);
866 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatIntsSkipped, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of skipped interrupts", vpciCounter(pcszNameFmt, "Interrupts/Skipped"), iInstance);
867 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatCsGC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling CS wait in GC", vpciCounter(pcszNameFmt, "Cs/CsGC"), iInstance);
868 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatCsHC, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling CS wait in HC", vpciCounter(pcszNameFmt, "Cs/CsHC"), iInstance);
869#endif /* VBOX_WITH_STATISTICS */
870
871 return rc;
872}
873
874/**
875 * Destruct PCI-related part of device.
876 *
877 * We need to free non-VM resources only.
878 *
879 * @returns VBox status.
880 * @param pState The device state structure.
881 */
882int vpciDestruct(VPCISTATE* pState)
883{
884 Log(("%s Destroying PCI instance\n", INSTANCE(pState)));
885
886 if (PDMCritSectIsInitialized(&pState->cs))
887 PDMR3CritSectDelete(&pState->cs);
888
889 return VINF_SUCCESS;
890}
891
892/**
893 * Device relocation callback.
894 *
895 * When this callback is called the device instance data, and if the
896 * device have a GC component, is being relocated, or/and the selectors
897 * have been changed. The device must use the chance to perform the
898 * necessary pointer relocations and data updates.
899 *
900 * Before the GC code is executed the first time, this function will be
901 * called with a 0 delta so GC pointer calculations can be one in one place.
902 *
903 * @param pDevIns Pointer to the device instance.
904 * @param offDelta The relocation delta relative to the old location.
905 *
906 * @remark A relocation CANNOT fail.
907 */
908void vpciRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
909{
910 VPCISTATE* pState = PDMINS_2_DATA(pDevIns, VPCISTATE*);
911 pState->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
912 // TBD
913}
914
915PVQUEUE vpciAddQueue(VPCISTATE* pState, unsigned uSize,
916 void (*pfnCallback)(void *pvState, PVQUEUE pQueue),
917 const char *pcszName)
918{
919 PVQUEUE pQueue = NULL;
920 /* Find an empty queue slot */
921 for (unsigned i = 0; i < pState->nQueues; i++)
922 {
923 if (pState->Queues[i].VRing.uSize == 0)
924 {
925 pQueue = &pState->Queues[i];
926 break;
927 }
928 }
929
930 if (!pQueue)
931 {
932 Log(("%s Too many queues being added, no empty slots available!\n", INSTANCE(pState)));
933 }
934 else
935 {
936 pQueue->VRing.uSize = uSize;
937 pQueue->VRing.addrDescriptors = 0;
938 pQueue->uPageNumber = 0;
939 pQueue->pfnCallback = pfnCallback;
940 pQueue->pcszName = pcszName;
941 }
942
943 return pQueue;
944}
945
946#endif /* IN_RING3 */
947
948#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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