VirtualBox

source: vbox/trunk/src/VBox/Devices/VirtIO/VirtioCore.cpp@ 84909

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

fix scm burn

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.4 KB
Line 
1/* $Id: VirtioCore.cpp 84882 2020-06-19 13:07:08Z vboxsync $ */
2
3/** @file
4 * VirtioCore - Virtio Core (PCI, feature & config mgt, queue mgt & proxy, notification mgt)
5 */
6
7/*
8 * Copyright (C) 2009-2020 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
24
25#include <iprt/assert.h>
26#include <iprt/uuid.h>
27#include <iprt/mem.h>
28#include <iprt/sg.h>
29#include <iprt/assert.h>
30#include <iprt/string.h>
31#include <iprt/param.h>
32#include <iprt/types.h>
33#include <VBox/log.h>
34#include <VBox/msi.h>
35#include <iprt/types.h>
36#include <VBox/AssertGuest.h>
37#include <VBox/vmm/pdmdev.h>
38#include "VirtioCore.h"
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44#define INSTANCE(a_pVirtio) ((a_pVirtio)->szInstance)
45#define VIRTQNAME(a_pVirtio, a_uVirtqNbr) ((a_pVirtio)->aVirtqState[(a_uVirtqNbr)].szVirtqName)
46#define IS_DRIVER_OK(a_pVirtio) ((a_pVirtio)->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
47#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, pVirtqState) \
48 (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, pVirtqState) == 0)
49
50/**
51 * This macro returns true if the @a a_offAccess and access length (@a
52 * a_cbAccess) are within the range of the mapped capability struct described by
53 * @a a_LocCapData.
54 *
55 * @param[in] a_offAccess The offset into the MMIO bar of the access.
56 * @param[in] a_cbAccess The access size.
57 * @param[out] a_offsetInMbr The variable to return the intra-capability
58 * offset into. ASSUMES this is uint32_t.
59 * @param[in] a_LocCapData The capability location info.
60 */
61#define MATCHES_VIRTIO_CAP_STRUCT(a_offAccess, a_cbAccess, a_offsetInMbr, a_LocCapData) \
62 ( ((a_offsetInMbr) = (uint32_t)((a_offAccess) - (a_LocCapData).offMmio)) < (uint32_t)(a_LocCapData).cbMmio \
63 && (a_offsetInMbr) + (uint32_t)(a_cbAccess) <= (uint32_t)(a_LocCapData).cbMmio )
64
65
66/** Marks the start of the virtio saved state (just for sanity). */
67#define VIRTIO_SAVEDSTATE_MARKER UINT64_C(0x1133557799bbddff)
68/** The current saved state version for the virtio core. */
69#define VIRTIO_SAVEDSTATE_VERSION UINT32_C(1)
70
71
72/*********************************************************************************************************************************
73* Structures and Typedefs *
74*********************************************************************************************************************************/
75
76
77/** @name virtq related flags
78 * @{ */
79#define VIRTQ_DESC_F_NEXT 1 /**< Indicates this descriptor chains to next */
80#define VIRTQ_DESC_F_WRITE 2 /**< Marks buffer as write-only (default ro) */
81#define VIRTQ_DESC_F_INDIRECT 4 /**< Buffer is list of buffer descriptors */
82
83#define VIRTQ_USED_F_NO_NOTIFY 1 /**< Dev to Drv: Don't notify when buf added */
84#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 /**< Drv to Dev: Don't notify when buf eaten */
85/** @} */
86
87/**
88 * virtq related structs
89 * (struct names follow VirtIO 1.0 spec, typedef use VBox style)
90 */
91typedef struct virtq_desc
92{
93 uint64_t GCPhysBuf; /**< addr GC Phys. address of buffer */
94 uint32_t cb; /**< len Buffer length */
95 uint16_t fFlags; /**< flags Buffer specific flags */
96 uint16_t uDescIdxNext; /**< next Idx set if VIRTIO_DESC_F_NEXT */
97} VIRTQ_DESC_T, *PVIRTQ_DESC_T;
98
99typedef struct virtq_avail
100{
101 uint16_t fFlags; /**< flags avail ring guest-to-host flags */
102 uint16_t uIdx; /**< idx Index of next free ring slot */
103 RT_FLEXIBLE_ARRAY_EXTENSION
104 uint16_t auRing[RT_FLEXIBLE_ARRAY]; /**< ring Ring: avail drv to dev bufs */
105 /* uint16_t uUsedEventIdx; - used_event (if VIRTQ_USED_F_EVENT_IDX) */
106} VIRTQ_AVAIL_T, *PVIRTQ_AVAIL_T;
107
108typedef struct virtq_used_elem
109{
110 uint32_t uDescIdx; /**< idx Start of used desc chain */
111 uint32_t cbElem; /**< len Total len of used desc chain */
112} VIRTQ_USED_ELEM_T;
113
114typedef struct virt_used
115{
116 uint16_t fFlags; /**< flags used ring host-to-guest flags */
117 uint16_t uIdx; /**< idx Index of next ring slot */
118 RT_FLEXIBLE_ARRAY_EXTENSION
119 VIRTQ_USED_ELEM_T aRing[RT_FLEXIBLE_ARRAY]; /**< ring Ring: used dev to drv bufs */
120 /* uint16_t uAvailEventIdx; - avail_event if (VIRTQ_USED_F_EVENT_IDX) */
121} VIRTQ_USED_T, *PVIRTQ_USED_T;
122
123
124const char *virtioCoreGetStateChangeText(VIRTIOVMSTATECHANGED enmState)
125{
126 switch (enmState)
127 {
128 case kvirtIoVmStateChangedReset: return "VM RESET";
129 case kvirtIoVmStateChangedSuspend: return "VM SUSPEND";
130 case kvirtIoVmStateChangedPowerOff: return "VM POWER OFF";
131 case kvirtIoVmStateChangedResume: return "VM RESUME";
132 default: return "<BAD ENUM>";
133 }
134}
135
136/* Internal Functions */
137
138static void virtioCoreNotifyGuestDriver(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr);
139static int virtioKick(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint8_t uCause, uint16_t uVec);
140
141/** @name Internal queue operations
142 * @{ */
143
144/**
145 * Accessor for virtq descriptor
146 */
147#ifdef IN_RING3
148DECLINLINE(void) virtioReadDesc(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr,
149 uint32_t idxDesc, PVIRTQ_DESC_T pDesc)
150{
151 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
152 uint16_t const cVirtqItems = RT_MAX(pVirtio->uVirtqSize[uVirtqNbr], 1); /* Make sure to avoid div-by-zero. */
153 PDMDevHlpPCIPhysRead(pDevIns,
154 pVirtio->aGCPhysVirtqDesc[uVirtqNbr] + sizeof(VIRTQ_DESC_T) * (idxDesc % cVirtqItems),
155 pDesc, sizeof(VIRTQ_DESC_T));
156}
157#endif
158
159/**
160 * Accessors for virtq avail ring
161 */
162#ifdef IN_RING3
163DECLINLINE(uint16_t) virtioReadAvailDescIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, uint32_t availIdx)
164{
165 uint16_t uDescIdx;
166 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
167 uint16_t const cVirtqItems = RT_MAX(pVirtio->uVirtqSize[uVirtqNbr], 1); /* Make sure to avoid div-by-zero. */
168 PDMDevHlpPCIPhysRead(pDevIns,
169 pVirtio->aGCPhysVirtqAvail[uVirtqNbr]
170 + RT_UOFFSETOF_DYN(VIRTQ_AVAIL_T, auRing[availIdx % cVirtqItems]),
171 &uDescIdx, sizeof(uDescIdx));
172 return uDescIdx;
173}
174
175DECLINLINE(uint16_t) virtioReadAvailUsedEvent(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
176{
177 uint16_t uUsedEventIdx;
178 /* VirtIO 1.0 uUsedEventIdx (used_event) immediately follows ring */
179 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
180 PDMDevHlpPCIPhysRead(pDevIns,
181 pVirtio->aGCPhysVirtqAvail[uVirtqNbr] + RT_UOFFSETOF_DYN(VIRTQ_AVAIL_T, auRing[pVirtio->uVirtqSize[uVirtqNbr]]),
182 &uUsedEventIdx, sizeof(uUsedEventIdx));
183 return uUsedEventIdx;
184}
185#endif
186
187DECLINLINE(uint16_t) virtioReadAvailRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
188{
189 uint16_t uIdx = 0;
190 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
191 PDMDevHlpPCIPhysRead(pDevIns,
192 pVirtio->aGCPhysVirtqAvail[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_AVAIL_T, uIdx),
193 &uIdx, sizeof(uIdx));
194 return uIdx;
195}
196
197DECLINLINE(uint16_t) virtioReadAvailRingFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
198{
199 uint16_t fFlags = 0;
200 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
201 PDMDevHlpPCIPhysRead(pDevIns,
202 pVirtio->aGCPhysVirtqAvail[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_AVAIL_T, fFlags),
203 &fFlags, sizeof(fFlags));
204 return fFlags;
205}
206
207/** @} */
208
209/** @name Accessors for virtq used ring
210 * @{
211 */
212
213#ifdef IN_RING3
214DECLINLINE(void) virtioWriteUsedElem(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr,
215 uint32_t usedIdx, uint32_t uDescIdx, uint32_t uLen)
216{
217 VIRTQ_USED_ELEM_T elem = { uDescIdx, uLen };
218 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
219 uint16_t const cVirtqItems = RT_MAX(pVirtio->uVirtqSize[uVirtqNbr], 1); /* Make sure to avoid div-by-zero. */
220 PDMDevHlpPCIPhysWrite(pDevIns,
221 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF_DYN(VIRTQ_USED_T, aRing[usedIdx % cVirtqItems]),
222 &elem, sizeof(elem));
223}
224
225DECLINLINE(void) virtioWriteUsedRingFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, uint16_t fFlags)
226{
227 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
228 RT_UNTRUSTED_VALIDATED_FENCE(); /* VirtIO 1.0, Section 3.2.1.4.1 */
229 PDMDevHlpPCIPhysWrite(pDevIns,
230 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_USED_T, fFlags),
231 &fFlags, sizeof(fFlags));
232}
233#endif
234
235DECLINLINE(void) virtioWriteUsedRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, uint16_t uIdx)
236{
237 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
238 PDMDevHlpPCIPhysWrite(pDevIns,
239 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_USED_T, uIdx),
240 &uIdx, sizeof(uIdx));
241}
242
243
244#ifdef IN_RING3
245
246DECLINLINE(uint16_t) virtioReadUsedRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
247{
248 uint16_t uIdx = 0;
249 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
250 PDMDevHlpPCIPhysRead(pDevIns,
251 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_USED_T, uIdx),
252 &uIdx, sizeof(uIdx));
253 return uIdx;
254}
255
256DECLINLINE(uint16_t) virtioReadUsedRingFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
257{
258 uint16_t fFlags = 0;
259 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
260 PDMDevHlpPCIPhysRead(pDevIns,
261 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF(VIRTQ_USED_T, fFlags),
262 &fFlags, sizeof(fFlags));
263 return fFlags;
264}
265
266DECLINLINE(void) virtioWriteUsedAvailEvent(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, uint32_t uAvailEventIdx)
267{
268 /** VirtIO 1.0 uAvailEventIdx (avail_event) immediately follows ring */
269 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
270 PDMDevHlpPCIPhysWrite(pDevIns,
271 pVirtio->aGCPhysVirtqUsed[uVirtqNbr] + RT_UOFFSETOF_DYN(VIRTQ_USED_T, aRing[pVirtio->uVirtqSize[uVirtqNbr]]),
272 &uAvailEventIdx, sizeof(uAvailEventIdx));
273}
274
275
276#endif
277
278DECLINLINE(uint16_t) virtioCoreVirtqAvailBufCount(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTQSTATE pVirtqState)
279{
280 uint16_t uIdx = virtioReadAvailRingIdx(pDevIns, pVirtio, pVirtqState->uVirtqNbr);
281 uint16_t uShadow = pVirtqState->uAvailIdxShadow;
282
283 uint16_t uDelta;
284 if (uIdx < uShadow)
285 uDelta = (uIdx + VIRTQ_MAX_ENTRIES) - uShadow;
286 else
287 uDelta = uIdx - uShadow;
288
289 LogFunc(("%s has %u %s (idx=%u shadow=%u)\n",
290 VIRTQNAME(pVirtio, pVirtqState->uVirtqNbr), uDelta, uDelta == 1 ? "entry" : "entries",
291 uIdx, uShadow));
292
293 return uDelta;
294}
295/**
296 * Get count of new (e.g. pending) elements in available ring.
297 *
298 * @param pDevIns The device instance.
299 * @param pVirtio Pointer to the shared virtio state.
300 * @param uVirtqNbr Virtq number
301 *
302 * @returns how many entries have been added to ring as a delta of the consumer's
303 * avail index and the queue's guest-side current avail index.
304 */
305uint16_t virtioCoreVirtqAvailBufCount(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
306{
307 if (!IS_DRIVER_OK(pVirtio) || !pVirtio->uVirtqEnable[uVirtqNbr])
308 {
309 LogRelFunc(("Driver not ready or queue not enabled\n"));
310 return 0;
311 }
312 return virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, &pVirtio->aVirtqState[uVirtqNbr]);
313}
314
315
316/** @} */
317
318void virtioCoreGCPhysChainInit(PVIRTIOSGBUF pGcSgBuf, PVIRTIOSGSEG paSegs, size_t cSegs)
319{
320 AssertPtr(pGcSgBuf);
321 Assert( (cSegs > 0 && VALID_PTR(paSegs)) || (!cSegs && !paSegs));
322 Assert(cSegs < (~(unsigned)0 >> 1));
323
324 pGcSgBuf->paSegs = paSegs;
325 pGcSgBuf->cSegs = (unsigned)cSegs;
326 pGcSgBuf->idxSeg = 0;
327 if (cSegs && paSegs)
328 {
329 pGcSgBuf->GCPhysCur = paSegs[0].GCPhys;
330 pGcSgBuf->cbSegLeft = paSegs[0].cbSeg;
331 }
332 else
333 {
334 pGcSgBuf->GCPhysCur = 0;
335 pGcSgBuf->cbSegLeft = 0;
336 }
337}
338
339static RTGCPHYS virtioCoreGCPhysChainGet(PVIRTIOSGBUF pGcSgBuf, size_t *pcbData)
340{
341 size_t cbData;
342 RTGCPHYS pGcBuf;
343
344 /* Check that the S/G buffer has memory left. */
345 if (RT_LIKELY(pGcSgBuf->idxSeg < pGcSgBuf->cSegs && pGcSgBuf->cbSegLeft))
346 { /* likely */ }
347 else
348 {
349 *pcbData = 0;
350 return 0;
351 }
352
353 AssertMsg( pGcSgBuf->cbSegLeft <= 128 * _1M
354 && (RTGCPHYS)pGcSgBuf->GCPhysCur >= (RTGCPHYS)pGcSgBuf->paSegs[pGcSgBuf->idxSeg].GCPhys
355 && (RTGCPHYS)pGcSgBuf->GCPhysCur + pGcSgBuf->cbSegLeft <=
356 (RTGCPHYS)pGcSgBuf->paSegs[pGcSgBuf->idxSeg].GCPhys + pGcSgBuf->paSegs[pGcSgBuf->idxSeg].cbSeg,
357 ("pGcSgBuf->idxSeg=%d pGcSgBuf->cSegs=%d pGcSgBuf->GCPhysCur=%p pGcSgBuf->cbSegLeft=%zd "
358 "pGcSgBuf->paSegs[%d].GCPhys=%p pGcSgBuf->paSegs[%d].cbSeg=%zd\n",
359 pGcSgBuf->idxSeg, pGcSgBuf->cSegs, pGcSgBuf->GCPhysCur, pGcSgBuf->cbSegLeft,
360 pGcSgBuf->idxSeg, pGcSgBuf->paSegs[pGcSgBuf->idxSeg].GCPhys, pGcSgBuf->idxSeg,
361 pGcSgBuf->paSegs[pGcSgBuf->idxSeg].cbSeg));
362
363 cbData = RT_MIN(*pcbData, pGcSgBuf->cbSegLeft);
364 pGcBuf = pGcSgBuf->GCPhysCur;
365 pGcSgBuf->cbSegLeft -= cbData;
366 if (!pGcSgBuf->cbSegLeft)
367 {
368 pGcSgBuf->idxSeg++;
369
370 if (pGcSgBuf->idxSeg < pGcSgBuf->cSegs)
371 {
372 pGcSgBuf->GCPhysCur = pGcSgBuf->paSegs[pGcSgBuf->idxSeg].GCPhys;
373 pGcSgBuf->cbSegLeft = pGcSgBuf->paSegs[pGcSgBuf->idxSeg].cbSeg;
374 }
375 *pcbData = cbData;
376 }
377 else
378 pGcSgBuf->GCPhysCur = pGcSgBuf->GCPhysCur + cbData;
379
380 return pGcBuf;
381}
382
383void virtioCoreGCPhysChainReset(PVIRTIOSGBUF pGcSgBuf)
384{
385 AssertPtrReturnVoid(pGcSgBuf);
386
387 pGcSgBuf->idxSeg = 0;
388 if (pGcSgBuf->cSegs)
389 {
390 pGcSgBuf->GCPhysCur = pGcSgBuf->paSegs[0].GCPhys;
391 pGcSgBuf->cbSegLeft = pGcSgBuf->paSegs[0].cbSeg;
392 }
393 else
394 {
395 pGcSgBuf->GCPhysCur = 0;
396 pGcSgBuf->cbSegLeft = 0;
397 }
398}
399
400RTGCPHYS virtioCoreGCPhysChainAdvance(PVIRTIOSGBUF pGcSgBuf, size_t cbAdvance)
401{
402 AssertReturn(pGcSgBuf, 0);
403
404 size_t cbLeft = cbAdvance;
405 while (cbLeft)
406 {
407 size_t cbThisAdvance = cbLeft;
408 virtioCoreGCPhysChainGet(pGcSgBuf, &cbThisAdvance);
409 if (!cbThisAdvance)
410 break;
411
412 cbLeft -= cbThisAdvance;
413 }
414 return cbAdvance - cbLeft;
415}
416
417RTGCPHYS virtioCoreGCPhysChainGetNextSegment(PVIRTIOSGBUF pGcSgBuf, size_t *pcbSeg)
418{
419 AssertReturn(pGcSgBuf, 0);
420 AssertPtrReturn(pcbSeg, 0);
421
422 if (!*pcbSeg)
423 *pcbSeg = pGcSgBuf->cbSegLeft;
424
425 return virtioCoreGCPhysChainGet(pGcSgBuf, pcbSeg);
426}
427
428size_t virtioCoreGCPhysChainCalcBufSize(PVIRTIOSGBUF pGcSgBuf)
429{
430 size_t cb = 0;
431 unsigned i = pGcSgBuf->cSegs;
432 while (i-- > 0)
433 cb += pGcSgBuf->paSegs[i].cbSeg;
434 return cb;
435 }
436
437#ifdef IN_RING3
438
439/** API Function: See header file*/
440void virtioCorePrintFeatures(VIRTIOCORE *pVirtio, PCDBGFINFOHLP pHlp)
441{
442 static struct
443 {
444 uint64_t fFeatureBit;
445 const char *pcszDesc;
446 } const s_aFeatures[] =
447 {
448 { VIRTIO_F_RING_INDIRECT_DESC, " RING_INDIRECT_DESC Driver can use descriptors with VIRTQ_DESC_F_INDIRECT flag set\n" },
449 { VIRTIO_F_RING_EVENT_IDX, " RING_EVENT_IDX Enables use_event and avail_event fields described in 2.4.7, 2.4.8\n" },
450 { VIRTIO_F_VERSION_1, " VERSION Used to detect legacy drivers.\n" },
451 };
452
453#define MAXLINE 80
454 /* Display as a single buf to prevent interceding log messages */
455 uint16_t cbBuf = RT_ELEMENTS(s_aFeatures) * 132;
456 char *pszBuf = (char *)RTMemAllocZ(cbBuf);
457 Assert(pszBuf);
458 char *cp = pszBuf;
459 for (unsigned i = 0; i < RT_ELEMENTS(s_aFeatures); ++i)
460 {
461 bool isOffered = RT_BOOL(pVirtio->uDeviceFeatures & s_aFeatures[i].fFeatureBit);
462 bool isNegotiated = RT_BOOL(pVirtio->uDriverFeatures & s_aFeatures[i].fFeatureBit);
463 cp += RTStrPrintf(cp, cbBuf - (cp - pszBuf), " %s %s %s",
464 isOffered ? "+" : "-", isNegotiated ? "x" : " ", s_aFeatures[i].pcszDesc);
465 }
466 if (pHlp)
467 pHlp->pfnPrintf(pHlp, "VirtIO Core Features Configuration\n\n"
468 " Offered Accepted Feature Description\n"
469 " ------- -------- ------- -----------\n"
470 "%s\n", pszBuf);
471#ifdef LOG_ENABLED
472 else
473 Log3(("VirtIO Core Features Configuration\n\n"
474 " Offered Accepted Feature Description\n"
475 " ------- -------- ------- -----------\n"
476 "%s\n", pszBuf));
477#endif
478 RTMemFree(pszBuf);
479}
480#endif
481
482#ifdef LOG_ENABLED
483
484/** API Function: See header file */
485void virtioCoreHexDump(uint8_t *pv, uint32_t cb, uint32_t uBase, const char *pszTitle)
486{
487#define ADJCURSOR(cb) pszOut += cb; cbRemain -= cb;
488 size_t cbPrint = 0, cbRemain = ((cb / 16) + 1) * 80;
489 char *pszBuf = (char *)RTMemAllocZ(cbRemain), *pszOut = pszBuf;
490 AssertMsgReturnVoid(pszBuf, ("Out of Memory"));
491 if (pszTitle)
492 {
493 cbPrint = RTStrPrintf(pszOut, cbRemain, "%s [%d bytes]:\n", pszTitle, cb);
494 ADJCURSOR(cbPrint);
495 }
496 for (uint32_t row = 0; row < RT_MAX(1, (cb / 16) + 1) && row * 16 < cb; row++)
497 {
498 cbPrint = RTStrPrintf(pszOut, cbRemain, "%04x: ", row * 16 + uBase); /* line address */
499 ADJCURSOR(cbPrint);
500 for (uint8_t col = 0; col < 16; col++)
501 {
502 uint32_t idx = row * 16 + col;
503 if (idx >= cb)
504 cbPrint = RTStrPrintf(pszOut, cbRemain, "-- %s", (col + 1) % 8 ? "" : " ");
505 else
506 cbPrint = RTStrPrintf(pszOut, cbRemain, "%02x %s", pv[idx], (col + 1) % 8 ? "" : " ");
507 ADJCURSOR(cbPrint);
508 }
509 for (uint32_t idx = row * 16; idx < row * 16 + 16; idx++)
510 {
511 cbPrint = RTStrPrintf(pszOut, cbRemain, "%c", (idx >= cb) ? ' ' : (pv[idx] >= 0x20 && pv[idx] <= 0x7e ? pv[idx] : '.'));
512 ADJCURSOR(cbPrint);
513 }
514 *pszOut++ = '\n';
515 --cbRemain;
516 }
517 Log(("%s\n", pszBuf));
518 RTMemFree(pszBuf);
519 RT_NOREF2(uBase, pv);
520#undef ADJCURSOR
521}
522
523/* API FUnction: See header file */
524void virtioCoreGCPhysHexDump(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, uint16_t cb, uint32_t uBase, const char *pszTitle)
525{
526#define ADJCURSOR(cb) pszOut += cb; cbRemain -= cb;
527 size_t cbPrint = 0, cbRemain = ((cb / 16) + 1) * 80;
528 char *pszBuf = (char *)RTMemAllocZ(cbRemain), *pszOut = pszBuf;
529 AssertMsgReturnVoid(pszBuf, ("Out of Memory"));
530 if (pszTitle)
531 {
532 cbPrint = RTStrPrintf(pszOut, cbRemain, "%s [%d bytes]:\n", pszTitle, cb);
533 ADJCURSOR(cbPrint);
534 }
535 for (uint16_t row = 0; row < (uint16_t)RT_MAX(1, (cb / 16) + 1) && row * 16 < cb; row++)
536 {
537 uint8_t c;
538 cbPrint = RTStrPrintf(pszOut, cbRemain, "%04x: ", row * 16 + uBase); /* line address */
539 ADJCURSOR(cbPrint);
540 for (uint8_t col = 0; col < 16; col++)
541 {
542 uint32_t idx = row * 16 + col;
543 PDMDevHlpPCIPhysRead(pDevIns, GCPhys + idx, &c, 1);
544 if (idx >= cb)
545 cbPrint = RTStrPrintf(pszOut, cbRemain, "-- %s", (col + 1) % 8 ? "" : " ");
546 else
547 cbPrint = RTStrPrintf(pszOut, cbRemain, "%02x %s", c, (col + 1) % 8 ? "" : " ");
548 ADJCURSOR(cbPrint);
549 }
550 for (uint16_t idx = row * 16; idx < row * 16 + 16; idx++)
551 {
552 PDMDevHlpPCIPhysRead(pDevIns, GCPhys + idx, &c, 1);
553 cbPrint = RTStrPrintf(pszOut, cbRemain, "%c", (idx >= cb) ? ' ' : (c >= 0x20 && c <= 0x7e ? c : '.'));
554 ADJCURSOR(cbPrint);
555 }
556 *pszOut++ = '\n';
557 --cbRemain;
558 }
559 Log(("%s\n", pszBuf));
560 RTMemFree(pszBuf);
561 RT_NOREF(uBase);
562#undef ADJCURSOR
563}
564#endif /* LOG_ENABLED */
565
566/** API function: See header file */
567void virtioCoreLogMappedIoValue(const char *pszFunc, const char *pszMember, uint32_t uMemberSize,
568 const void *pv, uint32_t cb, uint32_t uOffset, int fWrite,
569 int fHasIndex, uint32_t idx)
570{
571 if (!LogIs6Enabled())
572 return;
573
574 char szIdx[16];
575 if (fHasIndex)
576 RTStrPrintf(szIdx, sizeof(szIdx), "[%d]", idx);
577 else
578 szIdx[0] = '\0';
579
580 if (cb == 1 || cb == 2 || cb == 4 || cb == 8)
581 {
582 char szDepiction[64];
583 size_t cchDepiction;
584 if (uOffset != 0 || cb != uMemberSize) /* display bounds if partial member access */
585 cchDepiction = RTStrPrintf(szDepiction, sizeof(szDepiction), "%s%s[%d:%d]",
586 pszMember, szIdx, uOffset, uOffset + cb - 1);
587 else
588 cchDepiction = RTStrPrintf(szDepiction, sizeof(szDepiction), "%s%s", pszMember, szIdx);
589
590 /* padding */
591 if (cchDepiction < 30)
592 szDepiction[cchDepiction++] = ' ';
593 while (cchDepiction < 30)
594 szDepiction[cchDepiction++] = '.';
595 szDepiction[cchDepiction] = '\0';
596
597 RTUINT64U uValue;
598 uValue.u = 0;
599 memcpy(uValue.au8, pv, cb);
600 Log6(("%s: Guest %s %s %#0*RX64\n",
601 pszFunc, fWrite ? "wrote" : "read ", szDepiction, 2 + cb * 2, uValue.u));
602 }
603 else /* odd number or oversized access, ... log inline hex-dump style */
604 {
605 Log6(("%s: Guest %s %s%s[%d:%d]: %.*Rhxs\n",
606 pszFunc, fWrite ? "wrote" : "read ", pszMember,
607 szIdx, uOffset, uOffset + cb, cb, pv));
608 }
609 RT_NOREF2(fWrite, pszFunc);
610}
611
612
613/**
614 * Makes the MMIO-mapped Virtio uDeviceStatus registers non-cryptic
615 */
616DECLINLINE(void) virtioLogDeviceStatus(uint8_t bStatus)
617{
618 if (bStatus == 0)
619 Log6(("RESET"));
620 else
621 {
622 int primed = 0;
623 if (bStatus & VIRTIO_STATUS_ACKNOWLEDGE)
624 Log6(("%sACKNOWLEDGE", primed++ ? "" : ""));
625 if (bStatus & VIRTIO_STATUS_DRIVER)
626 Log6(("%sDRIVER", primed++ ? " | " : ""));
627 if (bStatus & VIRTIO_STATUS_FEATURES_OK)
628 Log6(("%sFEATURES_OK", primed++ ? " | " : ""));
629 if (bStatus & VIRTIO_STATUS_DRIVER_OK)
630 Log6(("%sDRIVER_OK", primed++ ? " | " : ""));
631 if (bStatus & VIRTIO_STATUS_FAILED)
632 Log6(("%sFAILED", primed++ ? " | " : ""));
633 if (bStatus & VIRTIO_STATUS_DEVICE_NEEDS_RESET)
634 Log6(("%sNEEDS_RESET", primed++ ? " | " : ""));
635 (void)primed;
636 }
637}
638
639#ifdef IN_RING3
640
641/** API Fuunction: See header file */
642void virtioCoreR3VirtqInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs, int uVirtqNbr)
643{
644 RT_NOREF(pszArgs);
645 PVIRTIOCORE pVirtio = PDMDEVINS_2_DATA(pDevIns, PVIRTIOCORE);
646 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
647
648 /** @todo add ability to dump physical contents of any descriptor (using existing VirtIO core API function) */
649// bool fDump = pszArgs && (*pszArgs == 'd' || *pszArgs == 'D'); /* "dump" (avail phys descriptor)"
650
651 uint16_t uAvailIdx = virtioReadAvailRingIdx(pDevIns, pVirtio, uVirtqNbr);
652 uint16_t uAvailIdxShadow = pVirtqState->uAvailIdxShadow;
653
654 uint16_t uUsedIdx = virtioReadUsedRingIdx(pDevIns, pVirtio, uVirtqNbr);
655 uint16_t uUsedIdxShadow = pVirtqState->uUsedIdxShadow;
656
657 PVIRTQBUF pVirtqBuf = NULL;
658
659 bool fEmpty = IS_VIRTQ_EMPTY(pDevIns, pVirtio, pVirtqState);
660
661 LogFunc(("%s, empty = %s\n", VIRTQNAME(pVirtio, uVirtqNbr), fEmpty ? "true" : "false"));
662
663 int cSendSegs = 0, cReturnSegs = 0;
664 if (!fEmpty)
665 {
666 virtioCoreR3VirtqAvailBufPeek(pDevIns, pVirtio, uVirtqNbr, &pVirtqBuf);
667 cSendSegs = pVirtqBuf->pSgPhysSend ? pVirtqBuf->pSgPhysSend->cSegs : 0;
668 cReturnSegs = pVirtqBuf->pSgPhysReturn ? pVirtqBuf->pSgPhysReturn->cSegs : 0;
669 }
670
671 bool fAvailNoInterrupt = virtioReadAvailRingFlags(pDevIns, pVirtio, uVirtqNbr) & VIRTQ_AVAIL_F_NO_INTERRUPT;
672 bool fUsedNoNotify = virtioReadUsedRingFlags(pDevIns, pVirtio, uVirtqNbr) & VIRTQ_USED_F_NO_NOTIFY;
673
674
675 pHlp->pfnPrintf(pHlp, " queue enabled: ........... %s\n", pVirtio->uVirtqEnable[uVirtqNbr] ? "true" : "false");
676 pHlp->pfnPrintf(pHlp, " size: .................... %d\n", pVirtio->uVirtqSize[uVirtqNbr]);
677 pHlp->pfnPrintf(pHlp, " notify offset: ........... %d\n", pVirtio->uVirtqNotifyOff[uVirtqNbr]);
678 if (pVirtio->fMsiSupport)
679 pHlp->pfnPrintf(pHlp, " MSIX vector: ....... %4.4x\n", pVirtio->uVirtqMsixVector[uVirtqNbr]);
680 pHlp->pfnPrintf(pHlp, "\n");
681 pHlp->pfnPrintf(pHlp, " avail ring (%d entries):\n", uAvailIdx - uAvailIdxShadow);
682 pHlp->pfnPrintf(pHlp, " index: ................ %d\n", uAvailIdx);
683 pHlp->pfnPrintf(pHlp, " shadow: ............... %d\n", uAvailIdxShadow);
684 pHlp->pfnPrintf(pHlp, " flags: ................ %s\n", fAvailNoInterrupt ? "NO_INTERRUPT" : "");
685 pHlp->pfnPrintf(pHlp, "\n");
686 pHlp->pfnPrintf(pHlp, " used ring (%d entries):\n", uUsedIdx - uUsedIdxShadow);
687 pHlp->pfnPrintf(pHlp, " index: ................ %d\n", uUsedIdx);
688 pHlp->pfnPrintf(pHlp, " shadow: ............... %d\n", uUsedIdxShadow);
689 pHlp->pfnPrintf(pHlp, " flags: ................ %s\n", fUsedNoNotify ? "NO_NOTIFY" : "");
690 pHlp->pfnPrintf(pHlp, "\n");
691 if (!fEmpty)
692 {
693 pHlp->pfnPrintf(pHlp, " desc chain:\n");
694 pHlp->pfnPrintf(pHlp, " head idx: ............. %d\n", uUsedIdx);
695 pHlp->pfnPrintf(pHlp, " segs: ................. %d\n", cSendSegs + cReturnSegs);
696 pHlp->pfnPrintf(pHlp, " refCnt ................ %d\n", pVirtqBuf->cRefs);
697 pHlp->pfnPrintf(pHlp, "\n");
698 pHlp->pfnPrintf(pHlp, " host-to-guest (%d bytes):\n", pVirtqBuf->cbPhysSend);
699 pHlp->pfnPrintf(pHlp, " segs: .............. %d\n", cSendSegs);
700 if (cSendSegs)
701 {
702 pHlp->pfnPrintf(pHlp, " index: ............. %d\n", pVirtqBuf->pSgPhysSend->idxSeg);
703 pHlp->pfnPrintf(pHlp, " unsent ............. %d\n", pVirtqBuf->pSgPhysSend->cbSegLeft);
704 }
705 pHlp->pfnPrintf(pHlp, "\n");
706 pHlp->pfnPrintf(pHlp, " guest-to-host (%d bytes)\n", pVirtqBuf->cbPhysReturn);
707 pHlp->pfnPrintf(pHlp, " segs: .............. %d\n", cReturnSegs);
708 if (cReturnSegs)
709 {
710 pHlp->pfnPrintf(pHlp, " index: ............. %d\n", pVirtqBuf->pSgPhysReturn->idxSeg);
711 pHlp->pfnPrintf(pHlp, " unsent ............. %d\n", pVirtqBuf->pSgPhysReturn->cbSegLeft);
712 }
713 } else
714 pHlp->pfnPrintf(pHlp, " No desc chains available\n");
715 pHlp->pfnPrintf(pHlp, "\n");
716
717}
718
719int virtioCoreR3VirtqAttach(PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, const char *pcszName)
720{
721 LogFunc(("%s\n", pcszName));
722 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
723 pVirtqState->uVirtqNbr = uVirtqNbr;
724 pVirtqState->uAvailIdxShadow = 0;
725 pVirtqState->uUsedIdxShadow = 0;
726 pVirtqState->fVirtqRingEventThreshold = false;
727 RTStrCopy(pVirtqState->szVirtqName, sizeof(pVirtqState->szVirtqName), pcszName);
728 return VINF_SUCCESS;
729}
730#endif /* IN_RING3 */
731
732
733#ifdef IN_RING3
734
735/** API Function: See header file */
736int virtioCoreR3VirtqAvailBufGet(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr,
737 uint16_t uHeadIdx, PPVIRTQBUF ppVirtqBuf)
738{
739 AssertReturn(ppVirtqBuf, VERR_INVALID_POINTER);
740 *ppVirtqBuf = NULL;
741
742 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
743
744 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
745
746 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uVirtqEnable[uVirtqNbr],
747 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
748
749 uint16_t uDescIdx = uHeadIdx;
750
751 Log6Func(("%s DESC CHAIN: (head) desc_idx=%u\n", pVirtqState->szVirtqName, uHeadIdx));
752 RT_NOREF(pVirtqState);
753
754 /*
755 * Allocate and initialize the descriptor chain structure.
756 */
757 PVIRTQBUF pVirtqBuf = (PVIRTQBUF)RTMemAllocZ(sizeof(VIRTQBUF_T));
758 AssertReturn(pVirtqBuf, VERR_NO_MEMORY);
759 pVirtqBuf->u32Magic = VIRTQBUF_MAGIC;
760 pVirtqBuf->cRefs = 1;
761 pVirtqBuf->uHeadIdx = uHeadIdx;
762 *ppVirtqBuf = pVirtqBuf;
763
764 /*
765 * Gather segments.
766 */
767 VIRTQ_DESC_T desc;
768
769 uint32_t cbIn = 0;
770 uint32_t cbOut = 0;
771 uint32_t cSegsIn = 0;
772 uint32_t cSegsOut = 0;
773 PVIRTIOSGSEG paSegsIn = pVirtqBuf->aSegsIn;
774 PVIRTIOSGSEG paSegsOut = pVirtqBuf->aSegsOut;
775
776 do
777 {
778 PVIRTIOSGSEG pSeg;
779
780 /*
781 * Malicious guests may go beyond paSegsIn or paSegsOut boundaries by linking
782 * several descriptors into a loop. Since there is no legitimate way to get a sequences of
783 * linked descriptors exceeding the total number of descriptors in the ring (see @bugref{8620}),
784 * the following aborts I/O if breach and employs a simple log throttling algorithm to notify.
785 */
786 if (cSegsIn + cSegsOut >= VIRTQ_MAX_ENTRIES)
787 {
788 static volatile uint32_t s_cMessages = 0;
789 static volatile uint32_t s_cThreshold = 1;
790 if (ASMAtomicIncU32(&s_cMessages) == ASMAtomicReadU32(&s_cThreshold))
791 {
792 LogRelMax(64, ("Too many linked descriptors; check if the guest arranges descriptors in a loop.\n"));
793 if (ASMAtomicReadU32(&s_cMessages) != 1)
794 LogRelMax(64, ("(the above error has occured %u times so far)\n", ASMAtomicReadU32(&s_cMessages)));
795 ASMAtomicWriteU32(&s_cThreshold, ASMAtomicReadU32(&s_cThreshold) * 10);
796 }
797 break;
798 }
799 RT_UNTRUSTED_VALIDATED_FENCE();
800
801 virtioReadDesc(pDevIns, pVirtio, uVirtqNbr, uDescIdx, &desc);
802
803 if (desc.fFlags & VIRTQ_DESC_F_WRITE)
804 {
805 Log6Func(("%s IN desc_idx=%u seg=%u addr=%RGp cb=%u\n", VIRTQNAME(pVirtio, uVirtqNbr), uDescIdx, cSegsIn, desc.GCPhysBuf, desc.cb));
806 cbIn += desc.cb;
807 pSeg = &paSegsIn[cSegsIn++];
808 }
809 else
810 {
811 Log6Func(("%s OUT desc_idx=%u seg=%u addr=%RGp cb=%u\n", VIRTQNAME(pVirtio, uVirtqNbr), uDescIdx, cSegsOut, desc.GCPhysBuf, desc.cb));
812 cbOut += desc.cb;
813 pSeg = &paSegsOut[cSegsOut++];
814 if (LogIs11Enabled())
815 {
816 virtioCoreGCPhysHexDump(pDevIns, desc.GCPhysBuf, desc.cb, 0, NULL);
817 Log(("\n"));
818 }
819 }
820
821 pSeg->GCPhys = desc.GCPhysBuf;
822 pSeg->cbSeg = desc.cb;
823
824 uDescIdx = desc.uDescIdxNext;
825 } while (desc.fFlags & VIRTQ_DESC_F_NEXT);
826
827 /*
828 * Add segments to the descriptor chain structure.
829 */
830 if (cSegsIn)
831 {
832 virtioCoreGCPhysChainInit(&pVirtqBuf->SgBufIn, paSegsIn, cSegsIn);
833 pVirtqBuf->pSgPhysReturn = &pVirtqBuf->SgBufIn;
834 pVirtqBuf->cbPhysReturn = cbIn;
835 STAM_REL_COUNTER_ADD(&pVirtio->StatDescChainsSegsIn, cSegsIn);
836 }
837
838 if (cSegsOut)
839 {
840 virtioCoreGCPhysChainInit(&pVirtqBuf->SgBufOut, paSegsOut, cSegsOut);
841 pVirtqBuf->pSgPhysSend = &pVirtqBuf->SgBufOut;
842 pVirtqBuf->cbPhysSend = cbOut;
843 STAM_REL_COUNTER_ADD(&pVirtio->StatDescChainsSegsOut, cSegsOut);
844 }
845
846 STAM_REL_COUNTER_INC(&pVirtio->StatDescChainsAllocated);
847 Log6Func(("%s -- segs OUT: %u (%u bytes) IN: %u (%u bytes) --\n", pVirtqState->szVirtqName, cSegsOut, cbOut, cSegsIn, cbIn));
848
849 return VINF_SUCCESS;
850}
851
852/** API Function: See header file */
853uint32_t virtioCoreR3VirtqBufRetain(PVIRTQBUF pVirtqBuf)
854{
855 AssertReturn(pVirtqBuf, UINT32_MAX);
856 AssertReturn(pVirtqBuf->u32Magic == VIRTQBUF_MAGIC, UINT32_MAX);
857 uint32_t cRefs = ASMAtomicIncU32(&pVirtqBuf->cRefs);
858 Assert(cRefs > 1);
859 Assert(cRefs < 16);
860 return cRefs;
861}
862
863
864/** API Function: See header file */
865uint32_t virtioCoreR3VirtqBufRelease(PVIRTIOCORE pVirtio, PVIRTQBUF pVirtqBuf)
866{
867 if (!pVirtqBuf)
868 return 0;
869 AssertReturn(pVirtqBuf, 0);
870 AssertReturn(pVirtqBuf->u32Magic == VIRTQBUF_MAGIC, 0);
871 uint32_t cRefs = ASMAtomicDecU32(&pVirtqBuf->cRefs);
872 Assert(cRefs < 16);
873 if (cRefs == 0)
874 {
875 pVirtqBuf->u32Magic = ~VIRTQBUF_MAGIC;
876 RTMemFree(pVirtqBuf);
877 STAM_REL_COUNTER_INC(&pVirtio->StatDescChainsFreed);
878 }
879 return cRefs;
880}
881
882/** API Function: See header file */
883void virtioCoreNotifyConfigChanged(PVIRTIOCORE pVirtio)
884{
885 virtioKick(pVirtio->pDevInsR3, pVirtio, VIRTIO_ISR_DEVICE_CONFIG, pVirtio->uMsixConfig);
886}
887
888void virtioCoreVirtqEnableNotify(PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, bool fEnable)
889{
890 if (pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
891 {
892 uint16_t fFlags = virtioReadUsedRingFlags(pVirtio->pDevInsR3, pVirtio, uVirtqNbr);
893
894 if (fEnable)
895 fFlags &= ~ VIRTQ_USED_F_NO_NOTIFY;
896 else
897 fFlags |= VIRTQ_USED_F_NO_NOTIFY;
898
899 virtioWriteUsedRingFlags(pVirtio->pDevInsR3, pVirtio, uVirtqNbr, fFlags);
900 }
901}
902
903/** API function: See Header file */
904void virtioCoreResetAll(PVIRTIOCORE pVirtio)
905{
906 LogFunc(("\n"));
907 pVirtio->uDeviceStatus |= VIRTIO_STATUS_DEVICE_NEEDS_RESET;
908 if (pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
909 {
910 pVirtio->fGenUpdatePending = true;
911 virtioKick(pVirtio->pDevInsR3, pVirtio, VIRTIO_ISR_DEVICE_CONFIG, pVirtio->uMsixConfig);
912 }
913}
914
915/** API function: See Header file */
916int virtioCoreR3VirtqAvailBufPeek(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr,
917 PPVIRTQBUF ppVirtqBuf)
918{
919 return virtioCoreR3VirtqAvailBufGet(pDevIns, pVirtio, uVirtqNbr, ppVirtqBuf, false);
920}
921
922/** API function: See Header file */
923int virtioCoreR3VirtqAvailBufNext(PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
924{
925 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
926 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
927
928 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uVirtqEnable[uVirtqNbr],
929 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
930
931 if (IS_VIRTQ_EMPTY(pVirtio->pDevInsR3, pVirtio, pVirtqState))
932 return VERR_NOT_AVAILABLE;
933
934 Log6Func(("%s avail shadow idx: %u\n", pVirtqState->szVirtqName, pVirtqState->uAvailIdxShadow));
935 pVirtqState->uAvailIdxShadow++;
936
937 return VINF_SUCCESS;
938}
939
940/** API function: See Header file */
941int virtioCoreR3VirtqAvailBufGet(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr,
942 PPVIRTQBUF ppVirtqBuf, bool fRemove)
943{
944 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
945
946 if (IS_VIRTQ_EMPTY(pDevIns, pVirtio, pVirtqState))
947 return VERR_NOT_AVAILABLE;
948
949 uint16_t uHeadIdx = virtioReadAvailDescIdx(pDevIns, pVirtio, uVirtqNbr, pVirtqState->uAvailIdxShadow);
950
951 if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
952 virtioWriteUsedAvailEvent(pDevIns,pVirtio, uVirtqNbr, pVirtqState->uAvailIdxShadow + 1);
953
954 if (fRemove)
955 pVirtqState->uAvailIdxShadow++;
956
957 int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, pVirtio, uVirtqNbr, uHeadIdx, ppVirtqBuf);
958 return rc;
959}
960
961/** API function: See Header file */
962int virtioCoreR3VirtqUsedBufPut(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, PRTSGBUF pSgVirtReturn,
963 PVIRTQBUF pVirtqBuf, bool fFence)
964{
965 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
966 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
967 PVIRTIOSGBUF pSgPhysReturn = pVirtqBuf->pSgPhysReturn;
968
969 Assert(pVirtqBuf->u32Magic == VIRTQBUF_MAGIC);
970 Assert(pVirtqBuf->cRefs > 0);
971
972 AssertMsgReturn(IS_DRIVER_OK(pVirtio), ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
973
974 Log6Func(("Copying client data to %s, desc chain (head desc_idx %d)\n",
975 VIRTQNAME(pVirtio, uVirtqNbr), virtioReadUsedRingIdx(pDevIns, pVirtio, uVirtqNbr)));
976
977 /* Copy s/g buf (virtual memory) to guest phys mem (IN direction). */
978
979 size_t cbCopy = 0, cbTotal = 0, cbRemain = 0;
980
981 if (pSgVirtReturn)
982 {
983 size_t cbTarget = virtioCoreGCPhysChainCalcBufSize(pSgPhysReturn);
984 cbRemain = cbTotal = RTSgBufCalcTotalLength(pSgVirtReturn);
985 AssertMsgReturn(cbTarget >= cbRemain, ("No space to write data to phys memory"), VERR_BUFFER_OVERFLOW);
986 virtioCoreGCPhysChainReset(pSgPhysReturn); /* Reset ptr because req data may have already been written */
987 while (cbRemain)
988 {
989 cbCopy = RT_MIN(pSgVirtReturn->cbSegLeft, pSgPhysReturn->cbSegLeft);
990 Assert(cbCopy > 0);
991 PDMDevHlpPhysWrite(pDevIns, (RTGCPHYS)pSgPhysReturn->GCPhysCur, pSgVirtReturn->pvSegCur, cbCopy);
992 RTSgBufAdvance(pSgVirtReturn, cbCopy);
993 virtioCoreGCPhysChainAdvance(pSgPhysReturn, cbCopy);
994 cbRemain -= cbCopy;
995 }
996
997 if (fFence)
998 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); /* needed? */
999
1000 Assert(!(cbCopy >> 32));
1001 }
1002
1003 /* If this write-ahead crosses threshold where the driver wants to get an event flag it */
1004 if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
1005 if (pVirtqState->uUsedIdxShadow == virtioReadAvailUsedEvent(pDevIns, pVirtio, uVirtqNbr))
1006 pVirtqState->fVirtqRingEventThreshold = true;
1007
1008 /*
1009 * Place used buffer's descriptor in used ring but don't update used ring's slot index.
1010 * That will be done with a subsequent client call to virtioCoreVirtqSyncUsedRing() */
1011 virtioWriteUsedElem(pDevIns, pVirtio, uVirtqNbr, pVirtqState->uUsedIdxShadow++, pVirtqBuf->uHeadIdx, (uint32_t)cbTotal);
1012
1013 if (pSgVirtReturn)
1014 Log6Func((".... Copied %zu bytes in %d segs to %u byte buffer, residual=%zu\n",
1015 cbTotal - cbRemain, pSgVirtReturn->cSegs, pVirtqBuf->cbPhysReturn, pVirtqBuf->cbPhysReturn - cbTotal));
1016
1017 Log6Func(("Write ahead used_idx=%u, %s used_idx=%u\n",
1018 pVirtqState->uUsedIdxShadow, VIRTQNAME(pVirtio, uVirtqNbr), virtioReadUsedRingIdx(pDevIns, pVirtio, uVirtqNbr)));
1019
1020 return VINF_SUCCESS;
1021}
1022
1023#endif /* IN_RING3 */
1024
1025/** API function: See Header file */
1026int virtioCoreVirtqSyncUsedRing(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
1027{
1028 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
1029 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
1030
1031 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uVirtqEnable[uVirtqNbr],
1032 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
1033
1034 Log6Func(("Updating %s used_idx to %u\n",
1035 VIRTQNAME(pVirtio, uVirtqNbr), pVirtqState->uUsedIdxShadow));
1036
1037 virtioWriteUsedRingIdx(pDevIns, pVirtio, uVirtqNbr, pVirtqState->uUsedIdxShadow);
1038 virtioCoreNotifyGuestDriver(pDevIns, pVirtio, uVirtqNbr);
1039
1040 return VINF_SUCCESS;
1041}
1042
1043
1044/**
1045 */
1046static void virtioCoreVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr, uint16_t uNotifyIdx)
1047{
1048
1049 PVIRTIOCORECC pVirtioCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1050
1051 /* See VirtIO 1.0, section 4.1.5.2 It implies that uVirtqNbr and uNotifyIdx should match.
1052 * Disregarding this notification may cause throughput to stop, however there's no way to know
1053 * which was queue was intended for wake-up if the two parameters disagree. */
1054
1055 AssertMsg(uNotifyIdx == uVirtqNbr,
1056 ("Guest kicked virtq %d's notify addr w/non-corresponding virtq idx %d\n",
1057 uVirtqNbr, uNotifyIdx));
1058 RT_NOREF(uNotifyIdx);
1059
1060 AssertReturnVoid(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
1061 Log6Func(("%s (desc chains: %u)\n",
1062 pVirtio->aVirtqState[uVirtqNbr].szVirtqName,
1063 virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr)));
1064
1065 /* Inform client */
1066 pVirtioCC->pfnVirtqNotified(pDevIns, pVirtio, uVirtqNbr);
1067}
1068
1069/**
1070 * Trigger MSI-X or INT# interrupt to notify guest of data added to used ring of
1071 * the specified virtq, depending on the interrupt configuration of the device
1072 * and depending on negotiated and realtime constraints flagged by the guest driver.
1073 *
1074 * See VirtIO 1.0 specification (section 2.4.7).
1075 *
1076 * @param pDevIns The device instance.
1077 * @param pVirtio Pointer to the shared virtio state.
1078 * @param uVirtqNbr Virtq to check for guest interrupt handling preference
1079 */
1080static void virtioCoreNotifyGuestDriver(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
1081{
1082
1083 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
1084 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
1085
1086 if (!IS_DRIVER_OK(pVirtio))
1087 {
1088 LogFunc(("Guest driver not in ready state.\n"));
1089 return;
1090 }
1091
1092 if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
1093 {
1094 if (pVirtqState->fVirtqRingEventThreshold)
1095 {
1096#ifdef IN_RING3
1097 Log6Func(("...kicking guest %s, VIRTIO_F_EVENT_IDX set and threshold (%d) reached\n",
1098 VIRTQNAME(pVirtio, uVirtqNbr), (uint16_t)virtioReadAvailUsedEvent(pDevIns, pVirtio, uVirtqNbr)));
1099#endif
1100 virtioKick(pDevIns, pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, pVirtio->uVirtqMsixVector[uVirtqNbr]);
1101 pVirtqState->fVirtqRingEventThreshold = false;
1102 return;
1103 }
1104#ifdef IN_RING3
1105 Log6Func(("...skip interrupt %s, VIRTIO_F_EVENT_IDX set but threshold (%d) not reached (%d)\n",
1106 VIRTQNAME(pVirtio, uVirtqNbr),(uint16_t)virtioReadAvailUsedEvent(pDevIns, pVirtio, uVirtqNbr), pVirtqState->uUsedIdxShadow));
1107#endif
1108 }
1109 else
1110 {
1111 /** If guest driver hasn't suppressed interrupts, interrupt */
1112 if (!(virtioReadAvailRingFlags(pDevIns, pVirtio, uVirtqNbr) & VIRTQ_AVAIL_F_NO_INTERRUPT))
1113 {
1114 virtioKick(pDevIns, pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, pVirtio->uVirtqMsixVector[uVirtqNbr]);
1115 return;
1116 }
1117 Log6Func(("...skipping interrupt for %s (guest set VIRTQ_AVAIL_F_NO_INTERRUPT)\n",
1118 VIRTQNAME(pVirtio, uVirtqNbr)));
1119 }
1120}
1121
1122/**
1123 * Raise interrupt or MSI-X
1124 *
1125 * @param pDevIns The device instance.
1126 * @param pVirtio Pointer to the shared virtio state.
1127 * @param uCause Interrupt cause bit mask to set in PCI ISR port.
1128 * @param uVec MSI-X vector, if enabled
1129 */
1130static int virtioKick(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint8_t uCause, uint16_t uMsixVector)
1131{
1132 if (uCause == VIRTIO_ISR_VIRTQ_INTERRUPT)
1133 Log6Func(("reason: buffer added to 'used' ring.\n"));
1134 else
1135 if (uCause == VIRTIO_ISR_DEVICE_CONFIG)
1136 Log6Func(("reason: device config change\n"));
1137
1138 if (!pVirtio->fMsiSupport)
1139 {
1140 pVirtio->uISR |= uCause;
1141 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
1142 }
1143 else if (uMsixVector != VIRTIO_MSI_NO_VECTOR)
1144 PDMDevHlpPCISetIrq(pDevIns, uMsixVector, 1);
1145 return VINF_SUCCESS;
1146}
1147
1148/**
1149 * Lower interrupt (Called when guest reads ISR and when resetting)
1150 *
1151 * @param pDevIns The device instance.
1152 */
1153static void virtioLowerInterrupt(PPDMDEVINS pDevIns, uint16_t uMsixVector)
1154{
1155 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1156 if (!pVirtio->fMsiSupport)
1157 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
1158 else if (uMsixVector != VIRTIO_MSI_NO_VECTOR)
1159 PDMDevHlpPCISetIrq(pDevIns, pVirtio->uMsixConfig, PDM_IRQ_LEVEL_LOW);
1160}
1161
1162#ifdef IN_RING3
1163static void virtioResetVirtq(PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
1164{
1165 Assert(uVirtqNbr < RT_ELEMENTS(pVirtio->aVirtqState));
1166 PVIRTQSTATE pVirtqState = &pVirtio->aVirtqState[uVirtqNbr];
1167 pVirtqState->uAvailIdxShadow = 0;
1168 pVirtqState->uUsedIdxShadow = 0;
1169 pVirtqState->fVirtqRingEventThreshold = false;
1170 pVirtio->uVirtqEnable[uVirtqNbr] = false;
1171 pVirtio->uVirtqSize[uVirtqNbr] = VIRTQ_MAX_ENTRIES;
1172 pVirtio->uVirtqNotifyOff[uVirtqNbr] = uVirtqNbr;
1173 pVirtio->uVirtqMsixVector[uVirtqNbr] = uVirtqNbr + 2;
1174 if (!pVirtio->fMsiSupport) /* VirtIO 1.0, 4.1.4.3 and 4.1.5.1.2 */
1175 pVirtio->uVirtqMsixVector[uVirtqNbr] = VIRTIO_MSI_NO_VECTOR;
1176
1177 virtioLowerInterrupt(pVirtio->pDevInsR3, pVirtio->uVirtqMsixVector[uVirtqNbr]);
1178}
1179
1180static void virtioResetDevice(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio)
1181{
1182 Log2Func(("\n"));
1183 pVirtio->uDeviceFeaturesSelect = 0;
1184 pVirtio->uDriverFeaturesSelect = 0;
1185 pVirtio->uConfigGeneration = 0;
1186 pVirtio->uDeviceStatus = 0;
1187 pVirtio->uISR = 0;
1188
1189 if (!pVirtio->fMsiSupport)
1190 virtioLowerInterrupt(pDevIns, 0);
1191 else
1192 {
1193 virtioLowerInterrupt(pDevIns, pVirtio->uMsixConfig);
1194 for (int i = 0; i < VIRTQ_MAX_CNT; i++)
1195 {
1196 virtioLowerInterrupt(pDevIns, pVirtio->uVirtqMsixVector[i]);
1197 pVirtio->uVirtqMsixVector[i];
1198 }
1199 }
1200
1201 if (!pVirtio->fMsiSupport) /* VirtIO 1.0, 4.1.4.3 and 4.1.5.1.2 */
1202 pVirtio->uMsixConfig = VIRTIO_MSI_NO_VECTOR;
1203
1204 for (uint16_t uVirtqNbr = 0; uVirtqNbr < VIRTQ_MAX_CNT; uVirtqNbr++)
1205 virtioResetVirtq(pVirtio, uVirtqNbr);
1206}
1207
1208/**
1209 * Invoked by this implementation when guest driver resets the device.
1210 * The driver itself will not until the device has read the status change.
1211 */
1212static void virtioGuestR3WasReset(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC)
1213{
1214 LogFunc(("Guest reset the device\n"));
1215
1216 /* Let the client know */
1217 pVirtioCC->pfnStatusChanged(pVirtio, pVirtioCC, 0);
1218 virtioResetDevice(pDevIns, pVirtio);
1219}
1220#endif /* IN_RING3 */
1221
1222/**
1223 * Handle accesses to Common Configuration capability
1224 *
1225 * @returns VBox status code
1226 *
1227 * @param pDevIns The device instance.
1228 * @param pVirtio Pointer to the shared virtio state.
1229 * @param pVirtioCC Pointer to the current context virtio state.
1230 * @param fWrite Set if write access, clear if read access.
1231 * @param offCfg The common configuration capability offset.
1232 * @param cb Number of bytes to read or write
1233 * @param pv Pointer to location to write to or read from
1234 */
1235static int virtioCommonCfgAccessed(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC,
1236 int fWrite, uint32_t offCfg, unsigned cb, void *pv)
1237{
1238/**
1239 * This macro resolves to boolean true if the implied parameters, offCfg and cb,
1240 * match the field offset and size of a field in the Common Cfg struct, (or if
1241 * it is a 64-bit field, if it accesses either 32-bit part as a 32-bit access)
1242 * This is mandated by section 4.1.3.1 of the VirtIO 1.0 specification)
1243 *
1244 * @param member Member of VIRTIO_PCI_COMMON_CFG_T
1245 * @param offCfg Implied parameter: Offset into VIRTIO_PCI_COMMON_CFG_T
1246 * @param cb Implied parameter: Number of bytes to access
1247 * @result true or false
1248 */
1249#define MATCH_COMMON_CFG(member) \
1250 ( ( RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member) == 8 \
1251 && ( offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) \
1252 || offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) + sizeof(uint32_t)) \
1253 && cb == sizeof(uint32_t)) \
1254 || ( offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) \
1255 && cb == RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member)) )
1256
1257#ifdef LOG_ENABLED
1258# define LOG_COMMON_CFG_ACCESS(member, uOffset) \
1259 if (LogIs7Enabled()) { \
1260 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member), \
1261 pv, cb, uOffset, fWrite, false, 0); \
1262 }
1263# define LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, uOffset) \
1264 if (LogIs7Enabled()) { \
1265 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member), \
1266 pv, cb, uOffset, fWrite, true, idx); \
1267 }
1268#else
1269# define LOG_COMMON_CFG_ACCESS(member, uOffset) do { } while (0)
1270# define LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, uOffset) do { } while (0)
1271#endif
1272
1273#define COMMON_CFG_ACCESSOR(member) \
1274 do \
1275 { \
1276 uint32_t uOffset = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
1277 if (fWrite) \
1278 memcpy((char *)&pVirtio->member + uOffset, (const char *)pv, cb); \
1279 else \
1280 memcpy(pv, (const char *)&pVirtio->member + uOffset, cb); \
1281 LOG_COMMON_CFG_ACCESS(member, uOffset); \
1282 } while(0)
1283
1284#define COMMON_CFG_ACCESSOR_INDEXED(member, idx) \
1285 do \
1286 { \
1287 uint32_t uOffset = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
1288 if (fWrite) \
1289 memcpy((char *)&pVirtio->member[idx] + uOffset, pv, cb); \
1290 else \
1291 memcpy(pv, (const char *)&pVirtio->member[idx] + uOffset, cb); \
1292 LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, uOffset); \
1293 } while(0)
1294
1295#define COMMON_CFG_ACCESSOR_READONLY(member) \
1296 do \
1297 { \
1298 uint32_t uOffset = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
1299 if (fWrite) \
1300 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
1301 else \
1302 { \
1303 memcpy(pv, (const char *)&pVirtio->member + uOffset, cb); \
1304 LOG_COMMON_CFG_ACCESS(member, uOffset); \
1305 } \
1306 } while(0)
1307
1308#define COMMON_CFG_ACCESSOR_INDEXED_READONLY(member, idx) \
1309 do \
1310 { \
1311 uint32_t uOffset = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
1312 if (fWrite) \
1313 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s[%d]\n", #member, idx)); \
1314 else \
1315 { \
1316 memcpy(pv, (char const *)&pVirtio->member[idx] + uOffset, cb); \
1317 LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, uOffset); \
1318 } \
1319 } while(0)
1320
1321
1322 int rc = VINF_SUCCESS;
1323 uint64_t val;
1324 if (MATCH_COMMON_CFG(uDeviceFeatures))
1325 {
1326 if (fWrite) /* Guest WRITE pCommonCfg>uDeviceFeatures */
1327 {
1328 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.device_feature\n"));
1329 return VINF_SUCCESS;
1330 }
1331 else /* Guest READ pCommonCfg->uDeviceFeatures */
1332 {
1333 switch (pVirtio->uDeviceFeaturesSelect)
1334 {
1335 case 0:
1336 val = pVirtio->uDeviceFeatures & UINT32_C(0xffffffff);
1337 memcpy(pv, &val, cb);
1338 LOG_COMMON_CFG_ACCESS(uDeviceFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDeviceFeatures));
1339 break;
1340 case 1:
1341 val = pVirtio->uDeviceFeatures >> 32;
1342 memcpy(pv, &val, cb);
1343 LOG_COMMON_CFG_ACCESS(uDeviceFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDeviceFeatures) + 4);
1344 break;
1345 default:
1346 LogFunc(("Guest read uDeviceFeatures with out of range selector (%#x), returning 0\n",
1347 pVirtio->uDeviceFeaturesSelect));
1348 return VINF_IOM_MMIO_UNUSED_00;
1349 }
1350 }
1351 }
1352 else if (MATCH_COMMON_CFG(uDriverFeatures))
1353 {
1354 if (fWrite) /* Guest WRITE pCommonCfg->udriverFeatures */
1355 {
1356 switch (pVirtio->uDriverFeaturesSelect)
1357 {
1358 case 0:
1359 memcpy(&pVirtio->uDriverFeatures, pv, cb);
1360 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures));
1361 break;
1362 case 1:
1363 memcpy((char *)&pVirtio->uDriverFeatures + sizeof(uint32_t), pv, cb);
1364 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures) + 4);
1365 break;
1366 default:
1367 LogFunc(("Guest wrote uDriverFeatures with out of range selector (%#x), returning 0\n",
1368 pVirtio->uDriverFeaturesSelect));
1369 return VINF_SUCCESS;
1370 }
1371 }
1372 else /* Guest READ pCommonCfg->udriverFeatures */
1373 {
1374 switch (pVirtio->uDriverFeaturesSelect)
1375 {
1376 case 0:
1377 val = pVirtio->uDriverFeatures & 0xffffffff;
1378 memcpy(pv, &val, cb);
1379 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures));
1380 break;
1381 case 1:
1382 val = (pVirtio->uDriverFeatures >> 32) & 0xffffffff;
1383 memcpy(pv, &val, cb);
1384 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures) + 4);
1385 break;
1386 default:
1387 LogFunc(("Guest read uDriverFeatures with out of range selector (%#x), returning 0\n",
1388 pVirtio->uDriverFeaturesSelect));
1389 return VINF_IOM_MMIO_UNUSED_00;
1390 }
1391 }
1392 }
1393 else if (MATCH_COMMON_CFG(uNumVirtqs))
1394 {
1395 if (fWrite)
1396 {
1397 Log2Func(("Guest attempted to write readonly virtio_pci_common_cfg.num_queues\n"));
1398 return VINF_SUCCESS;
1399 }
1400 else
1401 {
1402 *(uint16_t *)pv = VIRTQ_MAX_CNT;
1403 LOG_COMMON_CFG_ACCESS(uNumVirtqs, 0);
1404 }
1405 }
1406 else if (MATCH_COMMON_CFG(uDeviceStatus))
1407 {
1408 if (fWrite) /* Guest WRITE pCommonCfg->uDeviceStatus */
1409 {
1410 uint8_t const fNewStatus = *(uint8_t *)pv;
1411 Log7Func(("Guest wrote uDeviceStatus ................ ("));
1412 if (LogIs7Enabled())
1413 virtioLogDeviceStatus(fNewStatus ^ pVirtio->uDeviceStatus);
1414 Log7((")\n"));
1415
1416 /* If the status changed or we were reset, we need to go to ring-3 as
1417 it requires notifying the parent device. */
1418 bool const fStatusChanged = (fNewStatus & VIRTIO_STATUS_DRIVER_OK)
1419 != (pVirtio->uPrevDeviceStatus & VIRTIO_STATUS_DRIVER_OK);
1420#ifndef IN_RING3
1421 if (fStatusChanged || fNewStatus == 0)
1422 {
1423 Log6Func(("=>ring3\n"));
1424 return VINF_IOM_R3_MMIO_WRITE;
1425 }
1426#endif
1427 pVirtio->uDeviceStatus = fNewStatus;
1428
1429#ifdef IN_RING3
1430 /*
1431 * Notify client only if status actually changed from last time and when we're reset.
1432 */
1433 if (pVirtio->uDeviceStatus == 0)
1434 virtioGuestR3WasReset(pDevIns, pVirtio, pVirtioCC);
1435 if (fStatusChanged)
1436 pVirtioCC->pfnStatusChanged(pVirtio, pVirtioCC, fNewStatus & VIRTIO_STATUS_DRIVER_OK);
1437#endif
1438 /*
1439 * Save the current status for the next write so we can see what changed.
1440 */
1441 pVirtio->uPrevDeviceStatus = pVirtio->uDeviceStatus;
1442 }
1443 else /* Guest READ pCommonCfg->uDeviceStatus */
1444 {
1445 Log7Func(("Guest read uDeviceStatus ................ ("));
1446 *(uint8_t *)pv = pVirtio->uDeviceStatus;
1447 if (LogIs7Enabled())
1448 virtioLogDeviceStatus(pVirtio->uDeviceStatus);
1449 Log7((")\n"));
1450 }
1451 }
1452 else
1453 if (MATCH_COMMON_CFG(uMsixConfig))
1454 COMMON_CFG_ACCESSOR(uMsixConfig);
1455 else
1456 if (MATCH_COMMON_CFG(uDeviceFeaturesSelect))
1457 COMMON_CFG_ACCESSOR(uDeviceFeaturesSelect);
1458 else
1459 if (MATCH_COMMON_CFG(uDriverFeaturesSelect))
1460 COMMON_CFG_ACCESSOR(uDriverFeaturesSelect);
1461 else
1462 if (MATCH_COMMON_CFG(uConfigGeneration))
1463 COMMON_CFG_ACCESSOR_READONLY(uConfigGeneration);
1464 else
1465 if (MATCH_COMMON_CFG(uVirtqSelect))
1466 COMMON_CFG_ACCESSOR(uVirtqSelect);
1467 else
1468 if (MATCH_COMMON_CFG(uVirtqSize))
1469 COMMON_CFG_ACCESSOR_INDEXED(uVirtqSize, pVirtio->uVirtqSelect);
1470 else
1471 if (MATCH_COMMON_CFG(uVirtqMsixVector))
1472 COMMON_CFG_ACCESSOR_INDEXED(uVirtqMsixVector, pVirtio->uVirtqSelect);
1473 else
1474 if (MATCH_COMMON_CFG(uVirtqEnable))
1475 COMMON_CFG_ACCESSOR_INDEXED(uVirtqEnable, pVirtio->uVirtqSelect);
1476 else
1477 if (MATCH_COMMON_CFG(uVirtqNotifyOff))
1478 COMMON_CFG_ACCESSOR_INDEXED_READONLY(uVirtqNotifyOff, pVirtio->uVirtqSelect);
1479 else
1480 if (MATCH_COMMON_CFG(aGCPhysVirtqDesc))
1481 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysVirtqDesc, pVirtio->uVirtqSelect);
1482 else
1483 if (MATCH_COMMON_CFG(aGCPhysVirtqAvail))
1484 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysVirtqAvail, pVirtio->uVirtqSelect);
1485 else
1486 if (MATCH_COMMON_CFG(aGCPhysVirtqUsed))
1487 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysVirtqUsed, pVirtio->uVirtqSelect);
1488 else
1489 {
1490 Log2Func(("Bad guest %s access to virtio_pci_common_cfg: offCfg=%#x (%d), cb=%d\n",
1491 fWrite ? "write" : "read ", offCfg, offCfg, cb));
1492 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1493 }
1494
1495#undef COMMON_CFG_ACCESSOR_READONLY
1496#undef COMMON_CFG_ACCESSOR_INDEXED_READONLY
1497#undef COMMON_CFG_ACCESSOR_INDEXED
1498#undef COMMON_CFG_ACCESSOR
1499#undef LOG_COMMON_CFG_ACCESS_INDEXED
1500#undef LOG_COMMON_CFG_ACCESS
1501#undef MATCH_COMMON_CFG
1502#ifndef IN_RING3
1503 RT_NOREF(pDevIns, pVirtioCC);
1504#endif
1505 return rc;
1506}
1507
1508/**
1509 * @callback_method_impl{FNIOMMMIONEWREAD,
1510 * Memory mapped I/O Handler for PCI Capabilities read operations.}
1511 *
1512 * This MMIO handler specifically supports the VIRTIO_PCI_CAP_PCI_CFG capability defined
1513 * in the VirtIO 1.0 specification, section 4.1.4.7, and as such is restricted to reads
1514 * of 1, 2 or 4 bytes, only.
1515 *
1516 */
1517static DECLCALLBACK(VBOXSTRICTRC) virtioMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1518{
1519 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1520 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1521 AssertReturn(cb == 1 || cb == 2 || cb == 4, VERR_INVALID_PARAMETER);
1522 Assert(pVirtio == (PVIRTIOCORE)pvUser); RT_NOREF(pvUser);
1523
1524
1525 uint32_t uOffset;
1526 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocDeviceCap))
1527 {
1528#ifdef IN_RING3
1529 /*
1530 * Callback to client to manage device-specific configuration.
1531 */
1532 VBOXSTRICTRC rcStrict = pVirtioCC->pfnDevCapRead(pDevIns, uOffset, pv, cb);
1533
1534 /*
1535 * Additionally, anytime any part of the device-specific configuration (which our client maintains)
1536 * is READ it needs to be checked to see if it changed since the last time any part was read, in
1537 * order to maintain the config generation (see VirtIO 1.0 spec, section 4.1.4.3.1)
1538 */
1539 bool fDevSpecificFieldChanged = RT_BOOL(memcmp(pVirtioCC->pbDevSpecificCfg + uOffset,
1540 pVirtioCC->pbPrevDevSpecificCfg + uOffset,
1541 RT_MIN(cb, pVirtioCC->cbDevSpecificCfg - uOffset)));
1542
1543 memcpy(pVirtioCC->pbPrevDevSpecificCfg, pVirtioCC->pbDevSpecificCfg, pVirtioCC->cbDevSpecificCfg);
1544
1545 if (pVirtio->fGenUpdatePending || fDevSpecificFieldChanged)
1546 {
1547 ++pVirtio->uConfigGeneration;
1548 Log6Func(("Bumped cfg. generation to %d because %s%s\n",
1549 pVirtio->uConfigGeneration,
1550 fDevSpecificFieldChanged ? "<dev cfg changed> " : "",
1551 pVirtio->fGenUpdatePending ? "<update was pending>" : ""));
1552 pVirtio->fGenUpdatePending = false;
1553 }
1554
1555 virtioLowerInterrupt(pDevIns, 0);
1556 return rcStrict;
1557#else
1558 return VINF_IOM_R3_MMIO_READ;
1559#endif
1560 }
1561
1562 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocCommonCfgCap))
1563 return virtioCommonCfgAccessed(pDevIns, pVirtio, pVirtioCC, false /* fWrite */, uOffset, cb, pv);
1564
1565 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocIsrCap) && cb == sizeof(uint8_t))
1566 {
1567 *(uint8_t *)pv = pVirtio->uISR;
1568 Log6Func(("Read and clear ISR\n"));
1569 pVirtio->uISR = 0; /* VirtIO specification requires reads of ISR to clear it */
1570 virtioLowerInterrupt(pDevIns, 0);
1571 return VINF_SUCCESS;
1572 }
1573
1574 ASSERT_GUEST_MSG_FAILED(("Bad read access to mapped capabilities region: off=%RGp cb=%u\n", off, cb));
1575 return VINF_IOM_MMIO_UNUSED_00;
1576}
1577
1578/**
1579 * @callback_method_impl{FNIOMMMIONEWREAD,
1580 * Memory mapped I/O Handler for PCI Capabilities write operations.}
1581 *
1582 * This MMIO handler specifically supports the VIRTIO_PCI_CAP_PCI_CFG capability defined
1583 * in the VirtIO 1.0 specification, section 4.1.4.7, and as such is restricted to writes
1584 * of 1, 2 or 4 bytes, only.
1585 */
1586static DECLCALLBACK(VBOXSTRICTRC) virtioMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1587{
1588 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1589 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1590
1591 AssertReturn(cb == 1 || cb == 2 || cb == 4, VERR_INVALID_PARAMETER);
1592
1593 Assert(pVirtio == (PVIRTIOCORE)pvUser); RT_NOREF(pvUser);
1594 uint32_t uOffset;
1595 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocDeviceCap))
1596 {
1597#ifdef IN_RING3
1598 /*
1599 * Foreward this MMIO write access for client to deal with.
1600 */
1601 return pVirtioCC->pfnDevCapWrite(pDevIns, uOffset, pv, cb);
1602#else
1603 return VINF_IOM_R3_MMIO_WRITE;
1604#endif
1605 }
1606
1607 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocCommonCfgCap))
1608 return virtioCommonCfgAccessed(pDevIns, pVirtio, pVirtioCC, true /* fWrite */, uOffset, cb, (void *)pv);
1609
1610 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocIsrCap) && cb == sizeof(uint8_t))
1611 {
1612 pVirtio->uISR = *(uint8_t *)pv;
1613 Log6Func(("Setting uISR = 0x%02x (virtq interrupt: %d, dev confg interrupt: %d)\n",
1614 pVirtio->uISR & 0xff,
1615 pVirtio->uISR & VIRTIO_ISR_VIRTQ_INTERRUPT,
1616 RT_BOOL(pVirtio->uISR & VIRTIO_ISR_DEVICE_CONFIG)));
1617 return VINF_SUCCESS;
1618 }
1619
1620 /* This *should* be guest driver dropping index of a new descriptor in avail ring */
1621 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, uOffset, pVirtio->LocNotifyCap) && cb == sizeof(uint16_t))
1622 {
1623 virtioCoreVirtqNotified(pDevIns, pVirtio, uOffset / VIRTIO_NOTIFY_OFFSET_MULTIPLIER, *(uint16_t *)pv);
1624 return VINF_SUCCESS;
1625 }
1626
1627 ASSERT_GUEST_MSG_FAILED(("Bad write access to mapped capabilities region: off=%RGp pv=%#p{%.*Rhxs} cb=%u\n", off, pv, cb, pv, cb));
1628 return VINF_SUCCESS;
1629}
1630
1631#ifdef IN_RING3
1632
1633/**
1634 * @callback_method_impl{FNPCICONFIGREAD}
1635 */
1636static DECLCALLBACK(VBOXSTRICTRC) virtioR3PciConfigRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
1637 uint32_t uAddress, unsigned cb, uint32_t *pu32Value)
1638{
1639 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1640 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1641 RT_NOREF(pPciDev);
1642
1643 Log7Func(("pDevIns=%p pPciDev=%p uAddress=%#x cb=%u pu32Value=%p\n",
1644 pDevIns, pPciDev, uAddress, cb, pu32Value));
1645 if (uAddress == pVirtio->uPciCfgDataOff)
1646 {
1647 /*
1648 * VirtIO 1.0 spec section 4.1.4.7 describes a required alternative access capability
1649 * whereby the guest driver can specify a bar, offset, and length via the PCI configuration space
1650 * (the virtio_pci_cfg_cap capability), and access data items.
1651 */
1652 struct virtio_pci_cap *pPciCap = &pVirtioCC->pPciCfgCap->pciCap;
1653 uint32_t uLength = pPciCap->uLength;
1654
1655 if ( (uLength != 1 && uLength != 2 && uLength != 4)
1656 || cb != uLength
1657 || pPciCap->uBar != VIRTIO_REGION_PCI_CAP)
1658 {
1659 ASSERT_GUEST_MSG_FAILED(("Guest read virtio_pci_cfg_cap.pci_cfg_data using mismatching config. Ignoring\n"));
1660 *pu32Value = UINT32_MAX;
1661 return VINF_SUCCESS;
1662 }
1663
1664 VBOXSTRICTRC rcStrict = virtioMmioRead(pDevIns, pVirtio, pPciCap->uOffset, pu32Value, cb);
1665 Log2Func(("virtio: Guest read virtio_pci_cfg_cap.pci_cfg_data, bar=%d, offset=%d, length=%d, result=%d -> %Rrc\n",
1666 pPciCap->uBar, pPciCap->uOffset, uLength, *pu32Value, VBOXSTRICTRC_VAL(rcStrict)));
1667 return rcStrict;
1668 }
1669 return VINF_PDM_PCI_DO_DEFAULT;
1670}
1671
1672/**
1673 * @callback_method_impl{FNPCICONFIGWRITE}
1674 */
1675static DECLCALLBACK(VBOXSTRICTRC) virtioR3PciConfigWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
1676 uint32_t uAddress, unsigned cb, uint32_t u32Value)
1677{
1678 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1679 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1680 RT_NOREF(pPciDev);
1681
1682 Log7Func(("pDevIns=%p pPciDev=%p uAddress=%#x cb=%u u32Value=%#x\n", pDevIns, pPciDev, uAddress, cb, u32Value));
1683 if (uAddress == pVirtio->uPciCfgDataOff)
1684 {
1685 /* VirtIO 1.0 spec section 4.1.4.7 describes a required alternative access capability
1686 * whereby the guest driver can specify a bar, offset, and length via the PCI configuration space
1687 * (the virtio_pci_cfg_cap capability), and access data items. */
1688
1689 struct virtio_pci_cap *pPciCap = &pVirtioCC->pPciCfgCap->pciCap;
1690 uint32_t uLength = pPciCap->uLength;
1691
1692 if ( (uLength != 1 && uLength != 2 && uLength != 4)
1693 || cb != uLength
1694 || pPciCap->uBar != VIRTIO_REGION_PCI_CAP)
1695 {
1696 ASSERT_GUEST_MSG_FAILED(("Guest write virtio_pci_cfg_cap.pci_cfg_data using mismatching config. Ignoring\n"));
1697 return VINF_SUCCESS;
1698 }
1699
1700 VBOXSTRICTRC rcStrict = virtioMmioWrite(pDevIns, pVirtio, pPciCap->uOffset, &u32Value, cb);
1701 Log2Func(("Guest wrote virtio_pci_cfg_cap.pci_cfg_data, bar=%d, offset=%x, length=%x, value=%d -> %Rrc\n",
1702 pPciCap->uBar, pPciCap->uOffset, uLength, u32Value, VBOXSTRICTRC_VAL(rcStrict)));
1703 return rcStrict;
1704 }
1705 return VINF_PDM_PCI_DO_DEFAULT;
1706}
1707
1708
1709/*********************************************************************************************************************************
1710* Saved state. *
1711*********************************************************************************************************************************/
1712
1713/**
1714 * Called from the FNSSMDEVSAVEEXEC function of the device.
1715 *
1716 * @param pVirtio Pointer to the shared virtio state.
1717 * @param pHlp The ring-3 device helpers.
1718 * @param pSSM The saved state handle.
1719 * @returns VBox status code.
1720 */
1721int virtioCoreR3SaveExec(PVIRTIOCORE pVirtio, PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
1722{
1723 LogFunc(("\n"));
1724 pHlp->pfnSSMPutU64(pSSM, VIRTIO_SAVEDSTATE_MARKER);
1725 pHlp->pfnSSMPutU32(pSSM, VIRTIO_SAVEDSTATE_VERSION);
1726
1727 pHlp->pfnSSMPutBool(pSSM, pVirtio->fGenUpdatePending);
1728 pHlp->pfnSSMPutU8(pSSM, pVirtio->uDeviceStatus);
1729 pHlp->pfnSSMPutU8(pSSM, pVirtio->uConfigGeneration);
1730 pHlp->pfnSSMPutU8(pSSM, pVirtio->uPciCfgDataOff);
1731 pHlp->pfnSSMPutU8(pSSM, pVirtio->uISR);
1732 pHlp->pfnSSMPutU16(pSSM, pVirtio->uVirtqSelect);
1733 pHlp->pfnSSMPutU32(pSSM, pVirtio->uDeviceFeaturesSelect);
1734 pHlp->pfnSSMPutU32(pSSM, pVirtio->uDriverFeaturesSelect);
1735 pHlp->pfnSSMPutU64(pSSM, pVirtio->uDriverFeatures);
1736
1737 for (uint32_t i = 0; i < VIRTQ_MAX_CNT; i++)
1738 {
1739 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysVirtqDesc[i]);
1740 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysVirtqAvail[i]);
1741 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysVirtqUsed[i]);
1742 pHlp->pfnSSMPutU16(pSSM, pVirtio->uVirtqNotifyOff[i]);
1743 pHlp->pfnSSMPutU16(pSSM, pVirtio->uVirtqMsixVector[i]);
1744 pHlp->pfnSSMPutU16(pSSM, pVirtio->uVirtqEnable[i]);
1745 pHlp->pfnSSMPutU16(pSSM, pVirtio->uVirtqSize[i]);
1746 pHlp->pfnSSMPutU16(pSSM, pVirtio->aVirtqState[i].uAvailIdxShadow);
1747 pHlp->pfnSSMPutU16(pSSM, pVirtio->aVirtqState[i].uUsedIdxShadow);
1748 int rc = pHlp->pfnSSMPutMem(pSSM, pVirtio->aVirtqState[i].szVirtqName, 32);
1749 AssertRCReturn(rc, rc);
1750 }
1751
1752 return VINF_SUCCESS;
1753}
1754
1755/**
1756 * Called from the FNSSMDEVLOADEXEC function of the device.
1757 *
1758 * @param pVirtio Pointer to the shared virtio state.
1759 * @param pHlp The ring-3 device helpers.
1760 * @param pSSM The saved state handle.
1761 * @returns VBox status code.
1762 */
1763int virtioCoreR3LoadExec(PVIRTIOCORE pVirtio, PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
1764{
1765 LogFunc(("\n"));
1766 /*
1767 * Check the marker and (embedded) version number.
1768 */
1769 uint64_t uMarker = 0;
1770 int rc = pHlp->pfnSSMGetU64(pSSM, &uMarker);
1771 AssertRCReturn(rc, rc);
1772 if (uMarker != VIRTIO_SAVEDSTATE_MARKER)
1773 return pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
1774 N_("Expected marker value %#RX64 found %#RX64 instead"),
1775 VIRTIO_SAVEDSTATE_MARKER, uMarker);
1776 uint32_t uVersion = 0;
1777 rc = pHlp->pfnSSMGetU32(pSSM, &uVersion);
1778 AssertRCReturn(rc, rc);
1779 if (uVersion != VIRTIO_SAVEDSTATE_VERSION)
1780 return pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
1781 N_("Unsupported virtio version: %u"), uVersion);
1782 /*
1783 * Load the state.
1784 */
1785 pHlp->pfnSSMGetBool(pSSM, &pVirtio->fGenUpdatePending);
1786 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uDeviceStatus);
1787 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uConfigGeneration);
1788 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uPciCfgDataOff);
1789 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uISR);
1790 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uVirtqSelect);
1791 pHlp->pfnSSMGetU32(pSSM, &pVirtio->uDeviceFeaturesSelect);
1792 pHlp->pfnSSMGetU32(pSSM, &pVirtio->uDriverFeaturesSelect);
1793 pHlp->pfnSSMGetU64(pSSM, &pVirtio->uDriverFeatures);
1794
1795 for (uint32_t i = 0; i < VIRTQ_MAX_CNT; i++)
1796 {
1797 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysVirtqDesc[i]);
1798 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysVirtqAvail[i]);
1799 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysVirtqUsed[i]);
1800 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uVirtqNotifyOff[i]);
1801 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uVirtqMsixVector[i]);
1802 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uVirtqEnable[i]);
1803 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uVirtqSize[i]);
1804 pHlp->pfnSSMGetU16(pSSM, &pVirtio->aVirtqState[i].uAvailIdxShadow);
1805 pHlp->pfnSSMGetU16(pSSM, &pVirtio->aVirtqState[i].uUsedIdxShadow);
1806 rc = pHlp->pfnSSMGetMem(pSSM, pVirtio->aVirtqState[i].szVirtqName,
1807 sizeof(pVirtio->aVirtqState[i].szVirtqName));
1808 AssertRCReturn(rc, rc);
1809 }
1810
1811 return VINF_SUCCESS;
1812}
1813
1814
1815/*********************************************************************************************************************************
1816* Device Level *
1817*********************************************************************************************************************************/
1818
1819/**
1820 * This must be called by the client to handle VM state changes
1821 * after the client takes care of its device-specific tasks for the state change.
1822 * (i.e. Reset, suspend, power-off, resume)
1823 *
1824 * @param pDevIns The device instance.
1825 * @param pVirtio Pointer to the shared virtio state.
1826 */
1827void virtioCoreR3VmStateChanged(PVIRTIOCORE pVirtio, VIRTIOVMSTATECHANGED enmState)
1828{
1829 LogFunc(("State changing to %s\n",
1830 virtioCoreGetStateChangeText(enmState)));
1831
1832 switch(enmState)
1833 {
1834 case kvirtIoVmStateChangedReset:
1835 virtioCoreResetAll(pVirtio);
1836 break;
1837 case kvirtIoVmStateChangedSuspend:
1838 break;
1839 case kvirtIoVmStateChangedPowerOff:
1840 break;
1841 case kvirtIoVmStateChangedResume:
1842 virtioCoreNotifyGuestDriver(pVirtio->pDevInsR3, pVirtio, 0 /* uVirtqNbr */);
1843 break;
1844 default:
1845 LogRelFunc(("Bad enum value"));
1846 return;
1847 }
1848}
1849
1850/**
1851 * This should be called from PDMDEVREGR3::pfnDestruct.
1852 *
1853 * @param pDevIns The device instance.
1854 * @param pVirtio Pointer to the shared virtio state.
1855 * @param pVirtioCC Pointer to the ring-3 virtio state.
1856 */
1857void virtioCoreR3Term(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC)
1858{
1859 if (pVirtioCC->pbPrevDevSpecificCfg)
1860 {
1861 RTMemFree(pVirtioCC->pbPrevDevSpecificCfg);
1862 pVirtioCC->pbPrevDevSpecificCfg = NULL;
1863 }
1864 RT_NOREF(pDevIns, pVirtio);
1865}
1866
1867
1868/**rr
1869 * Setup PCI device controller and Virtio state
1870 *
1871 * This should be called from PDMDEVREGR3::pfnConstruct.
1872 *
1873 * @param pDevIns The device instance.
1874 * @param pVirtio Pointer to the shared virtio state. This
1875 * must be the first member in the shared
1876 * device instance data!
1877 * @param pVirtioCC Pointer to the ring-3 virtio state. This
1878 * must be the first member in the ring-3
1879 * device instance data!
1880 * @param pPciParams Values to populate industry standard PCI Configuration Space data structure
1881 * @param pcszInstance Device instance name (format-specifier)
1882 * @param fDevSpecificFeatures VirtIO device-specific features offered by
1883 * client
1884 * @param cbDevSpecificCfg Size of virtio_pci_device_cap device-specific struct
1885 * @param pvDevSpecificCfg Address of client's dev-specific
1886 * configuration struct.
1887 */
1888int virtioCoreR3Init(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, PVIRTIOPCIPARAMS pPciParams,
1889 const char *pcszInstance, uint64_t fDevSpecificFeatures, void *pvDevSpecificCfg, uint16_t cbDevSpecificCfg)
1890{
1891 /*
1892 * The pVirtio state must be the first member of the shared device instance
1893 * data, otherwise we cannot get our bearings in the PCI configuration callbacks.
1894 */
1895 AssertLogRelReturn(pVirtio == PDMINS_2_DATA(pDevIns, PVIRTIOCORE), VERR_STATE_CHANGED);
1896 AssertLogRelReturn(pVirtioCC == PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC), VERR_STATE_CHANGED);
1897
1898 pVirtio->pDevInsR3 = pDevIns;
1899
1900 /*
1901 * Caller must initialize these.
1902 */
1903 AssertReturn(pVirtioCC->pfnStatusChanged, VERR_INVALID_POINTER);
1904 AssertReturn(pVirtioCC->pfnVirtqNotified, VERR_INVALID_POINTER);
1905
1906#if 0 /* Until pdmR3DvHlp_PCISetIrq() impl is fixed and Assert that limits vec to 0 is removed */
1907# ifdef VBOX_WITH_MSI_DEVICES
1908 pVirtio->fMsiSupport = true;
1909# endif
1910#endif
1911
1912 /*
1913 * The host features offered include both device-specific features
1914 * and reserved feature bits (device independent)
1915 */
1916 pVirtio->uDeviceFeatures = VIRTIO_F_VERSION_1
1917 | VIRTIO_DEV_INDEPENDENT_FEATURES_OFFERED
1918 | fDevSpecificFeatures;
1919
1920 RTStrCopy(pVirtio->szInstance, sizeof(pVirtio->szInstance), pcszInstance);
1921
1922 pVirtio->uDeviceStatus = 0;
1923 pVirtioCC->cbDevSpecificCfg = cbDevSpecificCfg;
1924 pVirtioCC->pbDevSpecificCfg = (uint8_t *)pvDevSpecificCfg;
1925 pVirtioCC->pbPrevDevSpecificCfg = (uint8_t *)RTMemDup(pvDevSpecificCfg, cbDevSpecificCfg);
1926 AssertLogRelReturn(pVirtioCC->pbPrevDevSpecificCfg, VERR_NO_MEMORY);
1927
1928 /* Set PCI config registers (assume 32-bit mode) */
1929 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
1930 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
1931
1932 PDMPciDevSetRevisionId(pPciDev, DEVICE_PCI_REVISION_ID_VIRTIO);
1933 PDMPciDevSetVendorId(pPciDev, DEVICE_PCI_VENDOR_ID_VIRTIO);
1934 PDMPciDevSetSubSystemVendorId(pPciDev, DEVICE_PCI_VENDOR_ID_VIRTIO);
1935 PDMPciDevSetDeviceId(pPciDev, pPciParams->uDeviceId);
1936 PDMPciDevSetClassBase(pPciDev, pPciParams->uClassBase);
1937 PDMPciDevSetClassSub(pPciDev, pPciParams->uClassSub);
1938 PDMPciDevSetClassProg(pPciDev, pPciParams->uClassProg);
1939 PDMPciDevSetSubSystemId(pPciDev, pPciParams->uSubsystemId);
1940 PDMPciDevSetInterruptLine(pPciDev, pPciParams->uInterruptLine);
1941 PDMPciDevSetInterruptPin(pPciDev, pPciParams->uInterruptPin);
1942
1943 /* Register PCI device */
1944 int rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
1945 if (RT_FAILURE(rc))
1946 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio: cannot register PCI Device")); /* can we put params in this error? */
1947
1948 rc = PDMDevHlpPCIInterceptConfigAccesses(pDevIns, pPciDev, virtioR3PciConfigRead, virtioR3PciConfigWrite);
1949 AssertRCReturn(rc, rc);
1950
1951
1952 /* Construct & map PCI vendor-specific capabilities for virtio host negotiation with guest driver */
1953
1954 /* The following capability mapped via VirtIO 1.0: struct virtio_pci_cfg_cap (VIRTIO_PCI_CFG_CAP_T)
1955 * as a mandatory but suboptimal alternative interface to host device capabilities, facilitating
1956 * access the memory of any BAR. If the guest uses it (the VirtIO driver on Linux doesn't),
1957 * Unlike Common, Notify, ISR and Device capabilities, it is accessed directly via PCI Config region.
1958 * therefore does not contribute to the capabilities region (BAR) the other capabilities use.
1959 */
1960#define CFG_ADDR_2_IDX(addr) ((uint8_t)(((uintptr_t)(addr) - (uintptr_t)&pPciDev->abConfig[0])))
1961#define SET_PCI_CAP_LOC(a_pPciDev, a_pCfg, a_LocCap, a_uMmioLengthAlign) \
1962 do { \
1963 (a_LocCap).offMmio = (a_pCfg)->uOffset; \
1964 (a_LocCap).cbMmio = RT_ALIGN_T((a_pCfg)->uLength, a_uMmioLengthAlign, uint16_t); \
1965 (a_LocCap).offPci = (uint16_t)(uintptr_t)((uint8_t *)(a_pCfg) - &(a_pPciDev)->abConfig[0]); \
1966 (a_LocCap).cbPci = (a_pCfg)->uCapLen; \
1967 } while (0)
1968
1969 PVIRTIO_PCI_CAP_T pCfg;
1970 uint32_t cbRegion = 0;
1971
1972 /* Common capability (VirtIO 1.0 spec, section 4.1.4.3) */
1973 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[0x40];
1974 pCfg->uCfgType = VIRTIO_PCI_CAP_COMMON_CFG;
1975 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1976 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
1977 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
1978 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1979 pCfg->uOffset = RT_ALIGN_32(0, 4); /* reminder, in case someone changes offset */
1980 pCfg->uLength = sizeof(VIRTIO_PCI_COMMON_CFG_T);
1981 cbRegion += pCfg->uLength;
1982 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocCommonCfgCap, 2);
1983 pVirtioCC->pCommonCfgCap = pCfg;
1984
1985 /*
1986 * Notify capability (VirtIO 1.0 spec, section 4.1.4.4). Note: uLength is based on the choice
1987 * of this implementation to make each queue's uVirtqNotifyOff equal to (VirtqSelect) ordinal
1988 * value of the queue (different strategies are possible according to spec).
1989 */
1990 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
1991 pCfg->uCfgType = VIRTIO_PCI_CAP_NOTIFY_CFG;
1992 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1993 pCfg->uCapLen = sizeof(VIRTIO_PCI_NOTIFY_CAP_T);
1994 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
1995 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1996 pCfg->uOffset = pVirtioCC->pCommonCfgCap->uOffset + pVirtioCC->pCommonCfgCap->uLength;
1997 pCfg->uOffset = RT_ALIGN_32(pCfg->uOffset, 4);
1998
1999
2000 pCfg->uLength = VIRTQ_MAX_CNT * VIRTIO_NOTIFY_OFFSET_MULTIPLIER + 2; /* will change in VirtIO 1.1 */
2001 cbRegion += pCfg->uLength;
2002 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocNotifyCap, 1);
2003 pVirtioCC->pNotifyCap = (PVIRTIO_PCI_NOTIFY_CAP_T)pCfg;
2004 pVirtioCC->pNotifyCap->uNotifyOffMultiplier = VIRTIO_NOTIFY_OFFSET_MULTIPLIER;
2005
2006 /* ISR capability (VirtIO 1.0 spec, section 4.1.4.5)
2007 *
2008 * VirtIO 1.0 spec says 8-bit, unaligned in MMIO space. Example/diagram
2009 * of spec shows it as a 32-bit field with upper bits 'reserved'
2010 * Will take spec's words more literally than the diagram for now.
2011 */
2012 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
2013 pCfg->uCfgType = VIRTIO_PCI_CAP_ISR_CFG;
2014 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
2015 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
2016 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
2017 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
2018 pCfg->uOffset = pVirtioCC->pNotifyCap->pciCap.uOffset + pVirtioCC->pNotifyCap->pciCap.uLength;
2019 pCfg->uOffset = RT_ALIGN_32(pCfg->uOffset, 4);
2020 pCfg->uLength = sizeof(uint8_t);
2021 cbRegion += pCfg->uLength;
2022 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocIsrCap, 4);
2023 pVirtioCC->pIsrCap = pCfg;
2024
2025 /* PCI Cfg capability (VirtIO 1.0 spec, section 4.1.4.7)
2026 * This capability doesn't get page-MMIO mapped. Instead uBar, uOffset and uLength are intercepted
2027 * by trapping PCI configuration I/O and get modulated by consumers to locate fetch and read/write
2028 * values from any region. NOTE: The linux driver not only doesn't use this feature, it will not
2029 * even list it as present if uLength isn't non-zero and also 4-byte-aligned as the linux driver is
2030 * initializing.
2031 */
2032 pVirtio->uPciCfgDataOff = pCfg->uCapNext + RT_OFFSETOF(VIRTIO_PCI_CFG_CAP_T, uPciCfgData);
2033 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
2034 pCfg->uCfgType = VIRTIO_PCI_CAP_PCI_CFG;
2035 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
2036 pCfg->uCapLen = sizeof(VIRTIO_PCI_CFG_CAP_T);
2037 pCfg->uCapNext = (pVirtio->fMsiSupport || pVirtioCC->pbDevSpecificCfg) ? CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen : 0;
2038 pCfg->uBar = 0;
2039 pCfg->uOffset = 0;
2040 pCfg->uLength = 0;
2041 cbRegion += pCfg->uLength;
2042 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocPciCfgCap, 1);
2043 pVirtioCC->pPciCfgCap = (PVIRTIO_PCI_CFG_CAP_T)pCfg;
2044
2045 if (pVirtioCC->pbDevSpecificCfg)
2046 {
2047 /* Following capability (via VirtIO 1.0, section 4.1.4.6). Client defines the
2048 * device-specific config fields struct and passes size to this constructor */
2049 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
2050 pCfg->uCfgType = VIRTIO_PCI_CAP_DEVICE_CFG;
2051 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
2052 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
2053 pCfg->uCapNext = pVirtio->fMsiSupport ? CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen : 0;
2054 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
2055 pCfg->uOffset = pVirtioCC->pIsrCap->uOffset + pVirtioCC->pIsrCap->uLength;
2056 pCfg->uOffset = RT_ALIGN_32(pCfg->uOffset, 4);
2057 pCfg->uLength = cbDevSpecificCfg;
2058 cbRegion += pCfg->uLength;
2059 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocDeviceCap, 4);
2060 pVirtioCC->pDeviceCap = pCfg;
2061 }
2062 else
2063 Assert(pVirtio->LocDeviceCap.cbMmio == 0 && pVirtio->LocDeviceCap.cbPci == 0);
2064
2065 if (pVirtio->fMsiSupport)
2066 {
2067 PDMMSIREG aMsiReg;
2068 RT_ZERO(aMsiReg);
2069 aMsiReg.iMsixCapOffset = pCfg->uCapNext;
2070 aMsiReg.iMsixNextOffset = 0;
2071 aMsiReg.iMsixBar = VIRTIO_REGION_MSIX_CAP;
2072 aMsiReg.cMsixVectors = VBOX_MSIX_MAX_ENTRIES;
2073 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg); /* see MsixR3init() */
2074 if (RT_FAILURE(rc))
2075 {
2076 /* See PDMDevHlp.cpp:pdmR3DevHlp_PCIRegisterMsi */
2077 LogFunc(("Failed to configure MSI-X (%Rrc). Reverting to INTx\n", rc));
2078 pVirtio->fMsiSupport = false;
2079 }
2080 else
2081 Log2Func(("Using MSI-X for guest driver notification\n"));
2082 }
2083 else
2084 LogFunc(("MSI-X not available for VBox, using INTx notification\n"));
2085
2086 /* Set offset to first capability and enable PCI dev capabilities */
2087 PDMPciDevSetCapabilityList(pPciDev, 0x40);
2088 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
2089
2090 size_t cbSize = RTStrPrintf(pVirtioCC->pcszMmioName, sizeof(pVirtioCC->pcszMmioName), "%s MMIO", pcszInstance);
2091 if (cbSize <= 0)
2092 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio: out of memory allocating string")); /* can we put params in this error? */
2093
2094 /* Note: The Linux driver at drivers/virtio/virtio_pci_modern.c tries to map at least a page for the
2095 * 'unknown' device-specific capability without querying the capability to figure
2096 * out size, so pad with an extra page
2097 */
2098 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, VIRTIO_REGION_PCI_CAP, RT_ALIGN_32(cbRegion + PAGE_SIZE, PAGE_SIZE),
2099 PCI_ADDRESS_SPACE_MEM, virtioMmioWrite, virtioMmioRead, pVirtio,
2100 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
2101 pVirtioCC->pcszMmioName,
2102 &pVirtio->hMmioPciCap);
2103 AssertLogRelRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio: cannot register PCI Capabilities address space")));
2104 /*
2105 * Statistics.
2106 */
2107 PDMDevHlpSTAMRegisterF(pDevIns, &pVirtio->StatDescChainsAllocated, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
2108 "Total number of allocated descriptor chains", "DescChainsAllocated");
2109 PDMDevHlpSTAMRegisterF(pDevIns, &pVirtio->StatDescChainsFreed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
2110 "Total number of freed descriptor chains", "DescChainsFreed");
2111 PDMDevHlpSTAMRegisterF(pDevIns, &pVirtio->StatDescChainsSegsIn, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
2112 "Total number of inbound segments", "DescChainsSegsIn");
2113 PDMDevHlpSTAMRegisterF(pDevIns, &pVirtio->StatDescChainsSegsOut, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
2114 "Total number of outbound segments", "DescChainsSegsOut");
2115
2116 return VINF_SUCCESS;
2117}
2118
2119#else /* !IN_RING3 */
2120
2121/**
2122 * Sets up the core ring-0/raw-mode virtio bits.
2123 *
2124 * @returns VBox status code.
2125 * @param pDevIns The device instance.
2126 * @param pVirtio Pointer to the shared virtio state. This must be the first
2127 * member in the shared device instance data!
2128 */
2129int virtioCoreRZInit(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio)
2130{
2131 AssertLogRelReturn(pVirtio == PDMINS_2_DATA(pDevIns, PVIRTIOCORE), VERR_STATE_CHANGED);
2132
2133#ifdef FUTURE_OPTIMIZATION
2134 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
2135 AssertRCReturn(rc, rc);
2136#endif
2137 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pVirtio->hMmioPciCap, virtioMmioWrite, virtioMmioRead, pVirtio);
2138 AssertRCReturn(rc, rc);
2139 return rc;
2140}
2141
2142#endif /* !IN_RING3 */
2143
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