VirtualBox

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

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