VirtualBox

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

Last change on this file since 81720 was 81720, checked in by vboxsync, 5 years ago

DevVirtioNet,Virtio: Converted to new PDM device model. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.4 KB
Line 
1/* $Id: Virtio.cpp 81720 2019-11-06 20:23:17Z vboxsync $ */
2/** @file
3 * Virtio - Virtio Common Functions (VRing, VQueue, Virtio PCI)
4 */
5
6/*
7 * Copyright (C) 2009-2019 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
23
24#include <iprt/param.h>
25#include <iprt/uuid.h>
26#include <VBox/vmm/pdmdev.h>
27#include <VBox/AssertGuest.h>
28#include "Virtio.h"
29
30
31/*********************************************************************************************************************************
32* Defined Constants And Macros *
33*********************************************************************************************************************************/
34#define INSTANCE(pThis) (pThis->szInstance)
35
36
37static void vqueueReset(PVQUEUE pQueue)
38{
39 pQueue->VRing.addrDescriptors = 0;
40 pQueue->VRing.addrAvail = 0;
41 pQueue->VRing.addrUsed = 0;
42 pQueue->uNextAvailIndex = 0;
43 pQueue->uNextUsedIndex = 0;
44 pQueue->uPageNumber = 0;
45}
46
47static void vqueueInit(PVQUEUE pQueue, uint32_t uPageNumber)
48{
49 pQueue->VRing.addrDescriptors = (uint64_t)uPageNumber << PAGE_SHIFT;
50 pQueue->VRing.addrAvail = pQueue->VRing.addrDescriptors
51 + sizeof(VRINGDESC) * pQueue->VRing.uSize;
52 pQueue->VRing.addrUsed = RT_ALIGN(
53 pQueue->VRing.addrAvail + RT_UOFFSETOF_DYN(VRINGAVAIL, auRing[pQueue->VRing.uSize]),
54 PAGE_SIZE); /* The used ring must start from the next page. */
55 pQueue->uNextAvailIndex = 0;
56 pQueue->uNextUsedIndex = 0;
57}
58
59// void vqueueElemFree(PVQUEUEELEM pElem)
60// {
61// }
62
63void vringReadDesc(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex, PVRINGDESC pDesc)
64{
65 //Log(("%s vringReadDesc: ring=%p idx=%u\n", INSTANCE(pThis), pVRing, uIndex));
66 PDMDevHlpPhysRead(pDevIns,
67 pVRing->addrDescriptors + sizeof(VRINGDESC) * (uIndex % pVRing->uSize),
68 pDesc, sizeof(VRINGDESC));
69 /** @todo r=bird: Why exactly are we sometimes using PDMDevHlpPhysRead rather
70 * than PDMDevHlpPCIPhysRead? */
71}
72
73uint16_t vringReadAvail(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex)
74{
75 uint16_t tmp = 0;
76 PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF_DYN(VRINGAVAIL, auRing[uIndex % pVRing->uSize]),
77 &tmp, sizeof(tmp));
78 return tmp;
79}
80
81uint16_t vringReadAvailFlags(PPDMDEVINS pDevIns, PVRING pVRing)
82{
83 uint16_t tmp = 0;
84 PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF(VRINGAVAIL, uFlags), &tmp, sizeof(tmp));
85 return tmp;
86}
87
88void vringSetNotification(PPDMDEVINS pDevIns, PVRING pVRing, bool fEnabled)
89{
90 uint16_t fState = 0;
91 PDMDevHlpPhysRead(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uFlags), &fState, sizeof(fState));
92
93 if (fEnabled)
94 fState &= ~ VRINGUSED_F_NO_NOTIFY;
95 else
96 fState |= VRINGUSED_F_NO_NOTIFY;
97
98 PDMDevHlpPCIPhysWrite(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uFlags), &fState, sizeof(fState));
99}
100
101bool vqueueSkip(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
102{
103 if (vqueueIsEmpty(pDevIns, pQueue))
104 return false;
105
106 Log2(("%s vqueueSkip: %s avail_idx=%u\n", INSTANCE(pThis), pQueue->szName, pQueue->uNextAvailIndex));
107 pQueue->uNextAvailIndex++;
108 return true;
109}
110
111bool vqueueGet(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, bool fRemove)
112{
113 if (vqueueIsEmpty(pDevIns, pQueue))
114 return false;
115
116 pElem->nIn = pElem->nOut = 0;
117
118 Log2(("%s vqueueGet: %s avail_idx=%u\n", INSTANCE(pThis), pQueue->szName, pQueue->uNextAvailIndex));
119
120 VRINGDESC desc;
121 uint16_t idx = vringReadAvail(pDevIns, &pQueue->VRing, pQueue->uNextAvailIndex);
122 if (fRemove)
123 pQueue->uNextAvailIndex++;
124 pElem->uIndex = idx;
125 do
126 {
127 VQUEUESEG *pSeg;
128
129 /*
130 * Malicious guests may try to trick us into writing beyond aSegsIn or
131 * aSegsOut boundaries by linking several descriptors into a loop. We
132 * cannot possibly get a sequence of linked descriptors exceeding the
133 * total number of descriptors in the ring (see @bugref{8620}).
134 */
135 if (pElem->nIn + pElem->nOut >= VRING_MAX_SIZE)
136 {
137 static volatile uint32_t s_cMessages = 0;
138 static volatile uint32_t s_cThreshold = 1;
139 if (ASMAtomicIncU32(&s_cMessages) == ASMAtomicReadU32(&s_cThreshold))
140 {
141 LogRel(("%s: too many linked descriptors; check if the guest arranges descriptors in a loop.\n",
142 INSTANCE(pThis)));
143 if (ASMAtomicReadU32(&s_cMessages) != 1)
144 LogRel(("%s: (the above error has occured %u times so far)\n",
145 INSTANCE(pThis), ASMAtomicReadU32(&s_cMessages)));
146 ASMAtomicWriteU32(&s_cThreshold, ASMAtomicReadU32(&s_cThreshold) * 10);
147 }
148 break;
149 }
150 RT_UNTRUSTED_VALIDATED_FENCE();
151
152 vringReadDesc(pDevIns, &pQueue->VRing, idx, &desc);
153 if (desc.u16Flags & VRINGDESC_F_WRITE)
154 {
155 Log2(("%s vqueueGet: %s IN seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pThis),
156 pQueue->szName, pElem->nIn, idx, desc.u64Addr, desc.uLen));
157 pSeg = &pElem->aSegsIn[pElem->nIn++];
158 }
159 else
160 {
161 Log2(("%s vqueueGet: %s OUT seg=%u desc_idx=%u addr=%p cb=%u\n", INSTANCE(pThis),
162 pQueue->szName, pElem->nOut, idx, desc.u64Addr, desc.uLen));
163 pSeg = &pElem->aSegsOut[pElem->nOut++];
164 }
165
166 pSeg->addr = desc.u64Addr;
167 pSeg->cb = desc.uLen;
168 pSeg->pv = NULL;
169
170 idx = desc.u16Next;
171 } while (desc.u16Flags & VRINGDESC_F_NEXT);
172
173 Log2(("%s vqueueGet: %s head_desc_idx=%u nIn=%u nOut=%u\n", INSTANCE(pThis),
174 pQueue->szName, pElem->uIndex, pElem->nIn, pElem->nOut));
175 return true;
176}
177
178#ifdef LOG_ENABLED
179static uint16_t vringReadUsedIndex(PPDMDEVINS pDevIns, PVRING pVRing)
180{
181 uint16_t tmp = 0;
182 PDMDevHlpPhysRead(pDevIns, pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uIndex), &tmp, sizeof(tmp));
183 return tmp;
184}
185#endif
186
187static void vringWriteUsedIndex(PPDMDEVINS pDevIns, PVRING pVRing, uint16_t u16Value)
188{
189 PDMDevHlpPCIPhysWrite(pDevIns,
190 pVRing->addrUsed + RT_UOFFSETOF(VRINGUSED, uIndex),
191 &u16Value, sizeof(u16Value));
192}
193
194static void vringWriteUsedElem(PPDMDEVINS pDevIns, PVRING pVRing, uint32_t uIndex, uint32_t uId, uint32_t uLen)
195{
196 VRINGUSEDELEM elem;
197
198 elem.uId = uId;
199 elem.uLen = uLen;
200 PDMDevHlpPCIPhysWrite(pDevIns,
201 pVRing->addrUsed + RT_UOFFSETOF_DYN(VRINGUSED, aRing[uIndex % pVRing->uSize]),
202 &elem, sizeof(elem));
203}
204
205
206void vqueuePut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uTotalLen, uint32_t uReserved)
207{
208 Log2(("%s vqueuePut: %s desc_idx=%u acb=%u (%u)\n", INSTANCE(pThis), pQueue->szName, pElem->uIndex, uTotalLen, uReserved));
209
210 Assert(uReserved < uTotalLen);
211
212 uint32_t cbLen = uTotalLen - uReserved;
213 uint32_t cbSkip = uReserved;
214
215 for (unsigned i = 0; i < pElem->nIn && cbLen > 0; ++i)
216 {
217 if (cbSkip >= pElem->aSegsIn[i].cb) /* segment completely skipped? */
218 {
219 cbSkip -= pElem->aSegsIn[i].cb;
220 continue;
221 }
222
223 uint32_t cbSegLen = pElem->aSegsIn[i].cb - cbSkip;
224 if (cbSegLen > cbLen) /* last segment only partially used? */
225 cbSegLen = cbLen;
226
227 /*
228 * XXX: We should assert pv != NULL, but we need to check and
229 * fix all callers first.
230 */
231 if (pElem->aSegsIn[i].pv != NULL)
232 {
233 Log2(("%s vqueuePut: %s used_idx=%u seg=%u addr=%RGp pv=%p cb=%u acb=%u\n", INSTANCE(pThis), pQueue->szName,
234 pQueue->uNextUsedIndex, i, pElem->aSegsIn[i].addr, pElem->aSegsIn[i].pv, pElem->aSegsIn[i].cb, cbSegLen));
235
236 PDMDevHlpPCIPhysWrite(pDevIns,
237 pElem->aSegsIn[i].addr + cbSkip,
238 pElem->aSegsIn[i].pv,
239 cbSegLen);
240 }
241
242 cbSkip = 0;
243 cbLen -= cbSegLen;
244 }
245
246 Log2(("%s vqueuePut: %s used_idx=%u guest_used_idx=%u id=%u len=%u\n", INSTANCE(pThis), pQueue->szName,
247 pQueue->uNextUsedIndex, vringReadUsedIndex(pDevIns, &pQueue->VRing), pElem->uIndex, uTotalLen));
248
249 vringWriteUsedElem(pDevIns, &pQueue->VRing,
250 pQueue->uNextUsedIndex++,
251 pElem->uIndex, uTotalLen);
252}
253
254
255void vqueueNotify(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
256{
257 uint16_t const fAvail = vringReadAvailFlags(pDevIns, &pQueue->VRing);
258 LogFlow(("%s vqueueNotify: %s availFlags=%x guestFeatures=%x vqueue is %sempty\n", INSTANCE(pThis), pQueue->szName,
259 fAvail, pThis->uGuestFeatures, vqueueIsEmpty(pDevIns, pQueue)?"":"not "));
260 if ( !(fAvail & VRINGAVAIL_F_NO_INTERRUPT)
261 || ((pThis->uGuestFeatures & VPCI_F_NOTIFY_ON_EMPTY) && vqueueIsEmpty(pDevIns, pQueue)))
262 {
263 int rc = vpciRaiseInterrupt(pDevIns, pThis, VERR_INTERNAL_ERROR, VPCI_ISR_QUEUE);
264 if (RT_FAILURE(rc))
265 Log(("%s vqueueNotify: Failed to raise an interrupt (%Rrc).\n", INSTANCE(pThis), rc));
266 }
267 else
268 STAM_COUNTER_INC(&pThis->StatIntsSkipped);
269
270}
271
272void vqueueSync(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue)
273{
274 Log2(("%s vqueueSync: %s old_used_idx=%u new_used_idx=%u\n", INSTANCE(pThis),
275 pQueue->szName, vringReadUsedIndex(pDevIns, &pQueue->VRing), pQueue->uNextUsedIndex));
276 vringWriteUsedIndex(pDevIns, &pQueue->VRing, pQueue->uNextUsedIndex);
277 vqueueNotify(pDevIns, pThis, pQueue);
278}
279
280
281/**
282 * Raise interrupt.
283 *
284 * @param pDevIns The device instance.
285 * @param pThis The shared virtio core instance data.
286 * @param rcBusy Status code to return when the critical section is busy.
287 * @param u8IntCause Interrupt cause bit mask to set in PCI ISR port.
288 */
289int vpciRaiseInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy, uint8_t u8IntCause)
290{
291 RT_NOREF_PV(rcBusy);
292 // int rc = vpciCsEnter(pThis, rcBusy);
293 // if (RT_UNLIKELY(rc != VINF_SUCCESS))
294 // return rc;
295
296 STAM_COUNTER_INC(&pThis->StatIntsRaised);
297 LogFlow(("%s vpciRaiseInterrupt: u8IntCause=%x\n", INSTANCE(pThis), u8IntCause));
298
299 pThis->uISR |= u8IntCause;
300 PDMDevHlpPCISetIrq(pDevIns, 0, 1);
301 // vpciCsLeave(pThis);
302 return VINF_SUCCESS;
303}
304
305/**
306 * Lower interrupt.
307 *
308 * @param pDevIns The device instance.
309 * @param pThis The shared virtio core instance data.
310 */
311static void vpciLowerInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis)
312{
313 LogFlow(("%s vpciLowerInterrupt\n", INSTANCE(pThis)));
314 RT_NOREF(pThis);
315 PDMDevHlpPCISetIrq(pDevIns, 0, 0);
316}
317
318
319void vpciReset(PPDMDEVINS pDevIns, PVPCISTATE pThis)
320{
321 /* No interrupts should survive device reset, see @bugref(9556). */
322 if (pThis->uISR)
323 vpciLowerInterrupt(pDevIns, pThis);
324
325 pThis->uGuestFeatures = 0;
326 pThis->uQueueSelector = 0;
327 pThis->uStatus = 0;
328 pThis->uISR = 0;
329
330 for (unsigned i = 0; i < pThis->cQueues; i++)
331 vqueueReset(&pThis->Queues[i]);
332}
333
334
335DECLINLINE(uint32_t) vpciGetHostFeatures(PVPCISTATE pThis, PCVPCIIOCALLBACKS pCallbacks)
336{
337 return pCallbacks->pfnGetHostFeatures(pThis)
338 | VPCI_F_NOTIFY_ON_EMPTY;
339}
340
341/**
342 * Port I/O Handler for IN operations.
343 *
344 * @returns VBox status code.
345 *
346 * @param pDevIns The device instance.
347 * @param pThis The shared virtio core instance data.
348 * @param offPort The offset into the I/O range of the port being read.
349 * @param pu32 Where to store the result.
350 * @param cb Number of bytes read.
351 * @param pCallbacks Pointer to the callbacks.
352 * @thread EMT
353 */
354int vpciIOPortIn(PPDMDEVINS pDevIns,
355 PVPCISTATE pThis,
356 RTIOPORT offPort,
357 uint32_t *pu32,
358 unsigned cb,
359 PCVPCIIOCALLBACKS pCallbacks)
360{
361 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF(StatIORead), a);
362
363 /*
364 * We probably do not need to enter critical section when reading registers
365 * as the most of them are either constant or being changed during
366 * initialization only, the exception being ISR which can be raced by all
367 * threads but I see no big harm in it. It also happens to be the most read
368 * register as it gets read in interrupt handler. By dropping cs protection
369 * here we gain the ability to deliver RX packets to the guest while TX is
370 * holding cs transmitting queued packets.
371 *
372 int rc = vpciCsEnter(pThis, VINF_IOM_R3_IOPORT_READ);
373 if (RT_UNLIKELY(rc != VINF_SUCCESS))
374 {
375 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIORead), a);
376 return rc;
377 }*/
378 int rc = VINF_SUCCESS;
379
380 switch (offPort)
381 {
382 case VPCI_HOST_FEATURES:
383 /* Tell the guest what features we support. */
384 ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
385 *pu32 = vpciGetHostFeatures(pThis, pCallbacks) | VPCI_F_BAD_FEATURE;
386 break;
387
388 case VPCI_GUEST_FEATURES:
389 ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
390 *pu32 = pThis->uGuestFeatures;
391 break;
392
393 case VPCI_QUEUE_PFN:
394 ASSERT_GUEST_MSG(cb == 4, ("%d\n", cb));
395 *pu32 = pThis->Queues[pThis->uQueueSelector].uPageNumber;
396 break;
397
398 case VPCI_QUEUE_NUM:
399 ASSERT_GUEST_MSG(cb == 2, ("%d\n", cb));
400 *pu32 = pThis->Queues[pThis->uQueueSelector].VRing.uSize;
401 break;
402
403 case VPCI_QUEUE_SEL:
404 ASSERT_GUEST_MSG(cb == 2, ("%d\n", cb));
405 *pu32 = pThis->uQueueSelector;
406 break;
407
408 case VPCI_STATUS:
409 ASSERT_GUEST_MSG(cb == 1, ("%d\n", cb));
410 *pu32 = pThis->uStatus;
411 break;
412
413 case VPCI_ISR:
414 ASSERT_GUEST_MSG(cb == 1, ("%d\n", cb));
415 *pu32 = pThis->uISR;
416 pThis->uISR = 0; /* read clears all interrupts */
417 vpciLowerInterrupt(pDevIns, pThis);
418 break;
419
420 default:
421 if (offPort >= VPCI_CONFIG)
422 rc = pCallbacks->pfnGetConfig(pThis, offPort - VPCI_CONFIG, cb, pu32);
423 else
424 {
425 *pu32 = UINT32_MAX;
426 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortIn: no valid port at offset port=%RTiop cb=%08x\n",
427 INSTANCE(pThis), offPort, cb);
428 }
429 break;
430 }
431 Log3(("%s vpciIOPortIn: At %RTiop in %0*x\n", INSTANCE(pThis), offPort, cb*2, *pu32));
432
433 //vpciCsLeave(pThis);
434
435 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIORead), a);
436 return rc;
437}
438
439
440/**
441 * Port I/O Handler for OUT operations.
442 *
443 * @returns VBox status code.
444 *
445 * @param pDevIns The device instance.
446 * @param pThis The shared virtio core instance data.
447 * @param offPort The offset into the I/O range of the port being written.
448 * @param u32 The value to output.
449 * @param cb The value size in bytes.
450 * @param pCallbacks Pointer to the callbacks.
451 * @thread EMT
452 */
453int vpciIOPortOut(PPDMDEVINS pDevIns,
454 PVPCISTATE pThis,
455 PVPCISTATECC pThisCC,
456 RTIOPORT offPort,
457 uint32_t u32,
458 unsigned cb,
459 PCVPCIIOCALLBACKS pCallbacks)
460{
461 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF(StatIOWrite), a);
462 int rc = VINF_SUCCESS;
463 bool fHasBecomeReady;
464
465 Log3(("%s virtioIOPortOut: At offPort=%RTiop out %0*x\n", INSTANCE(pThis), offPort, cb*2, u32));
466
467 switch (offPort)
468 {
469 case VPCI_GUEST_FEATURES:
470 {
471 const uint32_t fHostFeatures = vpciGetHostFeatures(pThis, pCallbacks);
472
473 if (RT_LIKELY((u32 & ~fHostFeatures) == 0))
474 pThis->uGuestFeatures = u32;
475 else
476 {
477 /*
478 * Guest requests features we don't advertise. Stick
479 * to the minimum if negotiation looks completely
480 * botched, otherwise restrict to advertised features.
481 */
482 if (u32 & VPCI_F_BAD_FEATURE)
483 {
484 Log(("%s WARNING! Guest failed to negotiate properly (guest=%x)\n",
485 INSTANCE(pThis), u32));
486 pThis->uGuestFeatures = pCallbacks->pfnGetHostMinimalFeatures(pThis);
487 }
488 else
489 {
490 Log(("%s Guest asked for features host does not support! (host=%x guest=%x)\n",
491 INSTANCE(pThis), fHostFeatures, u32));
492 pThis->uGuestFeatures = u32 & fHostFeatures;
493 }
494 }
495 pCallbacks->pfnSetHostFeatures(pThis, pThis->uGuestFeatures);
496 break;
497 }
498
499 case VPCI_QUEUE_PFN:
500 /*
501 * The guest is responsible for allocating the pages for queues,
502 * here it provides us with the page number of descriptor table.
503 * Note that we provide the size of the queue to the guest via
504 * VIRTIO_PCI_QUEUE_NUM.
505 */
506 pThis->Queues[pThis->uQueueSelector].uPageNumber = u32;
507 if (u32)
508 vqueueInit(&pThis->Queues[pThis->uQueueSelector], u32);
509 else
510 rc = pCallbacks->pfnReset(pDevIns);
511 break;
512
513 case VPCI_QUEUE_SEL:
514 ASSERT_GUEST_MSG(cb == 2, ("cb=%u\n", cb));
515 u32 &= 0xFFFF;
516 if (u32 < pThis->cQueues)
517 pThis->uQueueSelector = u32;
518 else
519 Log3(("%s vpciIOPortOut: Invalid queue selector %08x\n", INSTANCE(pThis), u32));
520 break;
521
522 case VPCI_QUEUE_NOTIFY:
523#ifdef IN_RING3
524 ASSERT_GUEST_MSG(cb == 2, ("cb=%u\n", cb));
525 u32 &= 0xFFFF;
526 if (u32 < pThis->cQueues)
527 {
528 RT_UNTRUSTED_VALIDATED_FENCE();
529 if (pThis->Queues[u32].VRing.addrDescriptors)
530 {
531
532 // rc = vpciCsEnter(pThis, VERR_SEM_BUSY);
533 // if (RT_LIKELY(rc == VINF_SUCCESS))
534 // {
535 pThisCC->Queues[u32].pfnCallback(pDevIns, &pThis->Queues[u32]);
536 // vpciCsLeave(pThis);
537 // }
538 }
539 else
540 Log(("%s The queue (#%d) being notified has not been initialized.\n",
541 INSTANCE(pThis), u32));
542 }
543 else
544 Log(("%s Invalid queue number (%d)\n", INSTANCE(pThis), u32));
545#else
546 RT_NOREF(pThisCC);
547 rc = VINF_IOM_R3_IOPORT_WRITE;
548#endif
549 break;
550
551 case VPCI_STATUS:
552 ASSERT_GUEST_MSG(cb == 1, ("cb=%u\n", cb));
553 u32 &= 0xFF;
554 fHasBecomeReady = !(pThis->uStatus & VPCI_STATUS_DRV_OK) && (u32 & VPCI_STATUS_DRV_OK);
555 pThis->uStatus = u32;
556 /* Writing 0 to the status port triggers device reset. */
557 if (u32 == 0)
558 rc = pCallbacks->pfnReset(pDevIns);
559 else if (fHasBecomeReady)
560 {
561 /* Older hypervisors were lax and did not enforce bus mastering. Older guests
562 * (Linux prior to 2.6.34, NetBSD 6.x) were lazy and did not enable bus mastering.
563 * We automagically enable bus mastering on driver initialization to make existing
564 * drivers work.
565 */
566 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
567 PDMPciDevSetCommand(pPciDev, PDMPciDevGetCommand(pPciDev) | PCI_COMMAND_BUSMASTER);
568
569 pCallbacks->pfnReady(pDevIns);
570 }
571 break;
572
573 default:
574 if (offPort >= VPCI_CONFIG)
575 rc = pCallbacks->pfnSetConfig(pThis, offPort - VPCI_CONFIG, cb, &u32);
576 else
577 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s vpciIOPortOut: no valid port at offset offPort=%RTiop cb=%08x\n",
578 INSTANCE(pThis), offPort, cb);
579 break;
580 }
581
582 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF(StatIOWrite), a);
583 return rc;
584}
585
586#ifdef IN_RING3
587
588/**
589 * Handles common IBase.pfnQueryInterface requests.
590 */
591void *vpciR3QueryInterface(PVPCISTATECC pThisCC, const char *pszIID)
592{
593 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
594 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
595 return NULL;
596}
597
598/**
599 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
600 */
601static DECLCALLBACK(int) vpciR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
602{
603 PVPCISTATECC pThisCC = RT_FROM_MEMBER(pInterface, VPCISTATECC, ILeds);
604 if (iLUN == 0)
605 {
606 *ppLed = &pThisCC->pShared->led;
607 return VINF_SUCCESS;
608 }
609 return VERR_PDM_LUN_NOT_FOUND;
610}
611
612/**
613 * Turns on/off the write status LED.
614 *
615 * @returns VBox status code.
616 * @param pThis Pointer to the device state structure.
617 * @param fOn New LED state.
618 */
619void vpciR3SetWriteLed(PVPCISTATE pThis, bool fOn)
620{
621 LogFlow(("%s vpciR3SetWriteLed: %s\n", INSTANCE(pThis), fOn?"on":"off"));
622 if (fOn)
623 pThis->led.Asserted.s.fWriting = pThis->led.Actual.s.fWriting = 1;
624 else
625 pThis->led.Actual.s.fWriting = fOn;
626}
627
628/**
629 * Turns on/off the read status LED.
630 *
631 * @returns VBox status code.
632 * @param pThis Pointer to the device state structure.
633 * @param fOn New LED state.
634 */
635void vpciR3SetReadLed(PVPCISTATE pThis, bool fOn)
636{
637 LogFlow(("%s vpciR3SetReadLed: %s\n", INSTANCE(pThis), fOn?"on":"off"));
638 if (fOn)
639 pThis->led.Asserted.s.fReading = pThis->led.Actual.s.fReading = 1;
640 else
641 pThis->led.Actual.s.fReading = fOn;
642}
643
644
645# if 0 /* unused */
646/**
647 * Sets 32-bit register in PCI configuration space.
648 * @param refPciDev The PCI device.
649 * @param uOffset The register offset.
650 * @param u32Value The value to store in the register.
651 * @thread EMT
652 */
653DECLINLINE(void) vpciCfgSetU32(PDMPCIDEV& refPciDev, uint32_t uOffset, uint32_t u32Value)
654{
655 Assert(uOffset+sizeof(u32Value) <= sizeof(refPciDev.config));
656 *(uint32_t*)&refPciDev.config[uOffset] = u32Value;
657}
658# endif /* unused */
659
660
661/**
662 * Dumps the state (useful for both logging and info items).
663 */
664void vpcR3iDumpStateWorker(PVPCISTATE pThis, PCDBGFINFOHLP pHlp)
665{
666
667 pHlp->pfnPrintf(pHlp,
668 " uGuestFeatures = 0x%08x\n"
669 " uQueueSelector = 0x%04x\n"
670 " uStatus = 0x%02x\n"
671 " uISR = 0x%02x\n",
672 pThis->uGuestFeatures,
673 pThis->uQueueSelector,
674 pThis->uStatus,
675 pThis->uISR);
676
677 for (unsigned i = 0; i < pThis->cQueues; i++)
678 pHlp->pfnPrintf(pHlp,
679 " %s queue:\n"
680 " VRing.uSize = %u\n"
681 " VRing.addrDescriptors = %p\n"
682 " VRing.addrAvail = %p\n"
683 " VRing.addrUsed = %p\n"
684 " uNextAvailIndex = %u\n"
685 " uNextUsedIndex = %u\n"
686 " uPageNumber = %x\n",
687 pThis->Queues[i].szName,
688 pThis->Queues[i].VRing.uSize,
689 pThis->Queues[i].VRing.addrDescriptors,
690 pThis->Queues[i].VRing.addrAvail,
691 pThis->Queues[i].VRing.addrUsed,
692 pThis->Queues[i].uNextAvailIndex,
693 pThis->Queues[i].uNextUsedIndex,
694 pThis->Queues[i].uPageNumber);
695}
696
697# ifdef LOG_ENABLED
698void vpciR3DumpState(PVPCISTATE pThis, const char *pcszCaller)
699{
700 if (LogIs2Enabled())
701 {
702 Log2(("vpciR3DumpState: (called from %s)\n", pcszCaller));
703 vpcR3iDumpStateWorker(pThis, DBGFR3InfoLogHlp());
704 }
705}
706# else
707# define vpciR3DumpState(x, s) do {} while (0)
708# endif
709
710/**
711 * Saved the core virtio state.
712 *
713 * @returns VBox status code.
714 * @param pHlp The device helpers.
715 * @param pThis The shared virtio core instance data.
716 * @param pSSM The handle to the saved state.
717 */
718int vpciR3SaveExec(PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM)
719{
720 vpciR3DumpState(pThis, "vpciR3SaveExec");
721
722 pHlp->pfnSSMPutU32(pSSM, pThis->uGuestFeatures);
723 pHlp->pfnSSMPutU16(pSSM, pThis->uQueueSelector);
724 pHlp->pfnSSMPutU8( pSSM, pThis->uStatus);
725 pHlp->pfnSSMPutU8( pSSM, pThis->uISR);
726
727 /* Save queue states */
728 int rc = pHlp->pfnSSMPutU32(pSSM, pThis->cQueues);
729 AssertRCReturn(rc, rc);
730 for (unsigned i = 0; i < pThis->cQueues; i++)
731 {
732 pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].VRing.uSize);
733 pHlp->pfnSSMPutU32(pSSM, pThis->Queues[i].uPageNumber);
734 pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].uNextAvailIndex);
735 rc = pHlp->pfnSSMPutU16(pSSM, pThis->Queues[i].uNextUsedIndex);
736 AssertRCReturn(rc, rc);
737 }
738
739 return VINF_SUCCESS;
740}
741
742/**
743 * Loads a saved device state.
744 *
745 * @returns VBox status code.
746 * @param pHlp The device helpers.
747 * @param pThis The shared virtio core instance data.
748 * @param pSSM The handle to the saved state.
749 * @param uVersion The data unit version number.
750 * @param uPass The data pass.
751 * @param cQueues The default queue count (for old states).
752 */
753int vpciR3LoadExec(PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass, uint32_t cQueues)
754{
755 int rc;
756
757 if (uPass == SSM_PASS_FINAL)
758 {
759 /* Restore state data */
760 pHlp->pfnSSMGetU32(pSSM, &pThis->uGuestFeatures);
761 pHlp->pfnSSMGetU16(pSSM, &pThis->uQueueSelector);
762 pHlp->pfnSSMGetU8( pSSM, &pThis->uStatus);
763 pHlp->pfnSSMGetU8( pSSM, &pThis->uISR);
764
765 /* Restore queues */
766 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
767 {
768 rc = pHlp->pfnSSMGetU32(pSSM, &pThis->cQueues);
769 AssertRCReturn(rc, rc);
770 }
771 else
772 pThis->cQueues = cQueues;
773 AssertLogRelMsgReturn(pThis->cQueues <= VIRTIO_MAX_NQUEUES, ("%#x\n", pThis->cQueues), VERR_SSM_LOAD_CONFIG_MISMATCH);
774 AssertLogRelMsgReturn(pThis->uQueueSelector < pThis->cQueues || (pThis->cQueues == 0 && pThis->uQueueSelector),
775 ("uQueueSelector=%u cQueues=%u\n", pThis->uQueueSelector, pThis->cQueues),
776 VERR_SSM_LOAD_CONFIG_MISMATCH);
777
778 for (unsigned i = 0; i < pThis->cQueues; i++)
779 {
780 rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].VRing.uSize);
781 AssertRCReturn(rc, rc);
782 rc = pHlp->pfnSSMGetU32(pSSM, &pThis->Queues[i].uPageNumber);
783 AssertRCReturn(rc, rc);
784
785 if (pThis->Queues[i].uPageNumber)
786 vqueueInit(&pThis->Queues[i], pThis->Queues[i].uPageNumber);
787
788 rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].uNextAvailIndex);
789 AssertRCReturn(rc, rc);
790 rc = pHlp->pfnSSMGetU16(pSSM, &pThis->Queues[i].uNextUsedIndex);
791 AssertRCReturn(rc, rc);
792 }
793 }
794
795 vpciR3DumpState(pThis, "vpciLoadExec");
796
797 return VINF_SUCCESS;
798}
799
800/**
801 * Set PCI configuration space registers.
802 *
803 * @param pPciDev Pointer to the PCI device structure.
804 * @param uDeviceId VirtiO Device Id
805 * @param uClass Class of PCI device (network, etc)
806 * @thread EMT
807 */
808static void vpciConfigure(PPDMPCIDEV pPciDev, uint16_t uDeviceId, uint16_t uClass)
809{
810 /* Configure PCI Device, assume 32-bit mode ******************************/
811 PDMPciDevSetVendorId(pPciDev, DEVICE_PCI_VENDOR_ID);
812 PDMPciDevSetDeviceId(pPciDev, DEVICE_PCI_BASE_ID + uDeviceId);
813 PDMPciDevSetWord(pPciDev, VBOX_PCI_SUBSYSTEM_VENDOR_ID, DEVICE_PCI_SUBSYSTEM_VENDOR_ID);
814 PDMPciDevSetWord(pPciDev, VBOX_PCI_SUBSYSTEM_ID, DEVICE_PCI_SUBSYSTEM_BASE_ID + uDeviceId);
815
816 /* ABI version, must be equal 0 as of 2.6.30 kernel. */
817 PDMPciDevSetByte(pPciDev, VBOX_PCI_REVISION_ID, 0x00);
818 /* Ethernet adapter */
819 PDMPciDevSetByte(pPciDev, VBOX_PCI_CLASS_PROG, 0x00);
820 PDMPciDevSetWord(pPciDev, VBOX_PCI_CLASS_DEVICE, uClass);
821 /* Interrupt Pin: INTA# */
822 PDMPciDevSetByte(pPciDev, VBOX_PCI_INTERRUPT_PIN, 0x01);
823
824# ifdef VBOX_WITH_MSI_DEVICES
825 PDMPciDevSetCapabilityList(pPciDev, 0x80);
826 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
827# endif
828}
829
830
831int vpciR3Init(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, uint16_t uDeviceId, uint16_t uClass, uint32_t cQueues)
832{
833 /* Init data members. */
834 pThis->cQueues = cQueues;
835 pThis->led.u32Magic = PDMLED_MAGIC;
836 pThisCC->pShared = pThis;
837 pThisCC->ILeds.pfnQueryStatusLed = vpciR3QueryStatusLed;
838 AssertReturn(pThisCC->IBase.pfnQueryInterface, VERR_INVALID_POINTER);
839 AssertReturn(pThis->szInstance[0], VERR_INVALID_PARAMETER);
840 AssertReturn(strlen(pThis->szInstance) < sizeof(pThis->szInstance), VERR_INVALID_PARAMETER);
841
842 /* Initialize critical section. */
843 int rc = PDMDevHlpCritSectInit(pDevIns, &pThis->cs, RT_SRC_POS, "%s", pThis->szInstance);
844 AssertRCReturn(rc, rc);
845
846 /*
847 * Set up the PCI device.
848 */
849 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
850 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
851
852 /* Set PCI config registers */
853 vpciConfigure(pPciDev, uDeviceId, uClass);
854
855 /* Register PCI device */
856 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
857 AssertRCReturn(rc, rc);
858
859# ifdef VBOX_WITH_MSI_DEVICES
860# if 0
861 {
862 PDMMSIREG aMsiReg;
863
864 RT_ZERO(aMsiReg);
865 aMsiReg.cMsixVectors = 1;
866 aMsiReg.iMsixCapOffset = 0x80;
867 aMsiReg.iMsixNextOffset = 0x0;
868 aMsiReg.iMsixBar = 0;
869 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
870 if (RT_FAILURE (rc))
871 PCIDevSetCapabilityList(&pThis->pciDevice, 0x0);
872 }
873# endif
874# endif
875
876 /*
877 * Attach the status driver (optional).
878 */
879 PPDMIBASE pBase;
880 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
881 if (RT_SUCCESS(rc))
882 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
883 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
884 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
885
886 /*
887 * Statistics.
888 */
889# ifdef VBOX_WITH_STATISTICS
890 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, "IO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
891 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR0, STAMTYPE_PROFILE, "IO/ReadR0", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R0");
892 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRC, STAMTYPE_PROFILE, "IO/ReadRC", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RC");
893 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, "IO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
894 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR0, STAMTYPE_PROFILE, "IO/WriteR0", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R0");
895 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRC, STAMTYPE_PROFILE, "IO/WriteRC", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RC");
896 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsRaised, STAMTYPE_COUNTER, "Interrupts/Raised", STAMUNIT_OCCURENCES, "Number of raised interrupts");
897 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsSkipped, STAMTYPE_COUNTER, "Interrupts/Skipped", STAMUNIT_OCCURENCES, "Number of skipped interrupts");
898 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCsR3, STAMTYPE_PROFILE, "Cs/CsR3", STAMUNIT_TICKS_PER_CALL, "Profiling CS wait in R3");
899 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCsR0, STAMTYPE_PROFILE, "Cs/CsR0", STAMUNIT_TICKS_PER_CALL, "Profiling CS wait in R0");
900 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCsRC, STAMTYPE_PROFILE, "Cs/CsRC", STAMUNIT_TICKS_PER_CALL, "Profiling CS wait in RC");
901# endif /* VBOX_WITH_STATISTICS */
902
903 return VINF_SUCCESS;
904}
905
906/**
907 * Destruct PCI-related part of device.
908 *
909 * We need to free non-VM resources only.
910 *
911 * @returns VBox status code.
912 * @param pThis The shared virtio core instance data.
913 */
914int vpciR3Term(PPDMDEVINS pDevIns, PVPCISTATE pThis)
915{
916 Log(("%s Destroying PCI instance\n", INSTANCE(pThis)));
917
918 if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->cs))
919 PDMDevHlpCritSectDelete(pDevIns, &pThis->cs);
920
921 return VINF_SUCCESS;
922}
923
924PVQUEUE vpciR3AddQueue(PVPCISTATE pThis, PVPCISTATECC pThisCC, unsigned uSize,
925 PFNVPCIQUEUECALLBACK pfnCallback, const char *pcszName)
926{
927 /* Find an empty queue slot */
928 for (unsigned i = 0; i < pThis->cQueues; i++)
929 {
930 if (pThis->Queues[i].VRing.uSize == 0)
931 {
932 PVQUEUE pQueue = &pThis->Queues[i];
933 pQueue->VRing.uSize = uSize;
934 pQueue->VRing.addrDescriptors = 0;
935 pQueue->uPageNumber = 0;
936 int rc = RTStrCopy(pQueue->szName, sizeof(pQueue->szName), pcszName);
937 AssertRC(rc);
938 pThisCC->Queues[i].pfnCallback = pfnCallback;
939 return pQueue;
940 }
941 }
942 AssertMsgFailedReturn(("%s Too many queues being added, no empty slots available!\n", INSTANCE(pThis)), NULL);
943}
944
945#else /* !IN_RING3 */
946
947/**
948 * Does ring-0/raw-mode initialization.
949 */
950int vpciRZInit(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC)
951{
952 RT_NOREF(pDevIns, pThis, pThisCC);
953 return VINF_SUCCESS;
954}
955
956#endif /* !IN_RING3 */
957
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