VirtualBox

source: vbox/trunk/src/VBox/Devices/VirtIO/Virtio_1_0.cpp@ 81810

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

Virtio_1_0: indent fix (my bad). bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 78.5 KB
Line 
1/* $Id: Virtio_1_0.cpp 81690 2019-11-06 00:55:29Z vboxsync $ */
2/** @file
3 * Virtio_1_0 - Virtio Common (PCI, feature & config mgt, queue mgt & proxy, notification mgt)
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 <VBox/log.h>
25#include <VBox/msi.h>
26#include <VBox/AssertGuest.h>
27#include <iprt/param.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/mem.h>
31#include <iprt/assert.h>
32#include <iprt/sg.h>
33#include <iprt/string.h>
34#include <VBox/vmm/pdmdev.h>
35#include "Virtio_1_0.h"
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41#define INSTANCE(a_pVirtio) ((a_pVirtio)->szInstance)
42#define QUEUE_NAME(a_pVirtio, a_idxQueue) ((a_pVirtio)->virtqState[(a_idxQueue)].szVirtqName)
43#define IS_DRIVER_OK(a_pVirtio) ((a_pVirtio)->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
44
45/**
46 * This macro returns true if the @a a_offAccess and access length (@a
47 * a_cbAccess) are within the range of the mapped capability struct described by
48 * @a a_LocCapData.
49 *
50 * @param[in] a_offAccess The offset into the MMIO bar of the access.
51 * @param[in] a_cbAccess The access size.
52 * @param[out] a_offIntraVar The variable to return the intra-capability
53 * offset into. ASSUMES this is uint32_t.
54 * @param[in] a_LocCapData The capability location info.
55 */
56#define MATCHES_VIRTIO_CAP_STRUCT(a_offAccess, a_cbAccess, a_offIntraVar, a_LocCapData) \
57 ( ((a_offIntraVar) = (uint32_t)((a_offAccess) - (a_LocCapData).offMmio)) < (uint32_t)(a_LocCapData).cbMmio \
58 && (a_offIntraVar) + (uint32_t)(a_cbAccess) <= (uint32_t)(a_LocCapData).cbMmio )
59
60
61/** Marks the start of the virtio saved state (just for sanity). */
62#define VIRTIO_SAVEDSTATE_MARKER UINT64_C(0x1133557799bbddff)
63/** The current saved state version for the virtio core. */
64#define VIRTIO_SAVEDSTATE_VERSION UINT32_C(1)
65
66
67/*********************************************************************************************************************************
68* Structures and Typedefs *
69*********************************************************************************************************************************/
70/**
71 * virtq related structs
72 * (struct names follow VirtIO 1.0 spec, typedef use VBox style)
73 */
74typedef struct virtq_desc
75{
76 uint64_t GCPhysBuf; /**< addr GC Phys. address of buffer */
77 uint32_t cb; /**< len Buffer length */
78 uint16_t fFlags; /**< flags Buffer specific flags */
79 uint16_t uDescIdxNext; /**< next Idx set if VIRTIO_DESC_F_NEXT */
80} VIRTQ_DESC_T, *PVIRTQ_DESC_T;
81
82typedef struct virtq_avail
83{
84 uint16_t fFlags; /**< flags avail ring drv to dev flags */
85 uint16_t uIdx; /**< idx Index of next free ring slot */
86 uint16_t auRing[RT_FLEXIBLE_ARRAY]; /**< ring Ring: avail drv to dev bufs */
87 /* uint16_t uUsedEventIdx; - used_event (if VIRTQ_USED_F_EVENT_IDX) */
88} VIRTQ_AVAIL_T, *PVIRTQ_AVAIL_T;
89
90typedef struct virtq_used_elem
91{
92 uint32_t uDescIdx; /**< idx Start of used desc chain */
93 uint32_t cbElem; /**< len Total len of used desc chain */
94} VIRTQ_USED_ELEM_T;
95
96typedef struct virt_used
97{
98 uint16_t fFlags; /**< flags used ring host-to-guest flags */
99 uint16_t uIdx; /**< idx Index of next ring slot */
100 VIRTQ_USED_ELEM_T aRing[RT_FLEXIBLE_ARRAY]; /**< ring Ring: used dev to drv bufs */
101 /** @todo r=bird: From the usage, this member shouldn't be here and will only
102 * confuse compilers . */
103 /* uint16_t uAvailEventIdx; - avail_event if (VIRTQ_USED_F_EVENT_IDX) */
104} VIRTQ_USED_T, *PVIRTQ_USED_T;
105
106
107/*********************************************************************************************************************************
108* Internal Functions *
109*********************************************************************************************************************************/
110static void virtioNotifyGuestDriver(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, bool fForce);
111static int virtioKick(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint8_t uCause, uint16_t uVec, bool fForce);
112
113/** @name Internal queue operations
114 * @{ */
115
116#if 0 /* unused */
117DECLINLINE(int) virtqIsEventNeeded(uint16_t uEventIdx, uint16_t uDescIdxNew, uint16_t uDescIdxOld)
118{
119 return (uint16_t)(uDescIdxNew - uEventIdx - 1) < (uint16_t)(uDescIdxNew - uDescIdxOld);
120}
121#endif
122
123/**
124 * Accessor for virtq descriptor
125 */
126DECLINLINE(void) virtioReadDesc(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue,
127 uint32_t idxDesc, PVIRTQ_DESC_T pDesc)
128{
129 //Log(("%s virtioQueueReadDesc: ring=%p idx=%u\n", INSTANCE(pState), pVirtQ, idx));
130 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
131 uint16_t const cQueueItems = RT_MAX(pVirtio->uQueueSize[idxQueue], 1); /* Make sure to avoid div-by-zero. */
132 PDMDevHlpPhysRead(pDevIns, /** @todo r=bird: PDMDevHlpPhysRead or PDMDevHlpPCIPhysRead ?!? (ditto rest of file + writes) */
133 pVirtio->aGCPhysQueueDesc[idxQueue] + sizeof(VIRTQ_DESC_T) * (idxDesc % cQueueItems),
134 pDesc, sizeof(VIRTQ_DESC_T));
135}
136
137/**
138 * Accessors for virtq avail ring
139 */
140DECLINLINE(uint16_t) virtioReadAvailDescIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, uint32_t availIdx)
141{
142 uint16_t uDescIdx;
143 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
144 uint16_t const cQueueItems = RT_MAX(pVirtio->uQueueSize[idxQueue], 1); /* Make sure to avoid div-by-zero. */
145 PDMDevHlpPhysRead(pDevIns,
146 pVirtio->aGCPhysQueueAvail[idxQueue]
147 + RT_UOFFSETOF_DYN(VIRTQ_AVAIL_T, auRing[availIdx % cQueueItems]),
148 &uDescIdx, sizeof(uDescIdx));
149 return uDescIdx;
150}
151
152DECLINLINE(uint16_t) virtioReadAvailRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
153{
154 uint16_t uIdx = 0;
155 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
156 PDMDevHlpPhysRead(pDevIns,
157 pVirtio->aGCPhysQueueAvail[idxQueue] + RT_UOFFSETOF(VIRTQ_AVAIL_T, uIdx),
158 &uIdx, sizeof(uIdx));
159 return uIdx;
160}
161
162DECLINLINE(bool) virtqIsEmpty(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
163{
164 return virtioReadAvailRingIdx(pDevIns, pVirtio, idxQueue) == pVirtio->virtqState[idxQueue].uAvailIdx;
165}
166
167#if 0 /* unused */
168DECLINLINE(uint16_t) virtioReadAvailFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
169{
170 uint16_t fFlags;
171 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
172 PDMDevHlpPhysRead(pDevIns,
173 pVirtio->aGCPhysQueueAvail[idxQueue] + RT_UOFFSETOF(VIRTQ_AVAIL_T, fFlags),
174 &fFlags, sizeof(fFlags));
175 return fFlags;
176}
177#endif
178
179DECLINLINE(uint16_t) virtioReadAvailUsedEvent(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
180{
181 uint16_t uUsedEventIdx;
182 /* VirtIO 1.0 uUsedEventIdx (used_event) immediately follows ring */
183 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
184 PDMDevHlpPhysRead(pDevIns,
185 pVirtio->aGCPhysQueueAvail[idxQueue] + RT_UOFFSETOF_DYN(VIRTQ_AVAIL_T, auRing[pVirtio->uQueueSize[idxQueue]]),
186 &uUsedEventIdx, sizeof(uUsedEventIdx));
187 return uUsedEventIdx;
188}
189/** @} */
190
191/** @name Accessors for virtq used ring
192 * @{
193 */
194DECLINLINE(void) virtioWriteUsedElem(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue,
195 uint32_t usedIdx, uint32_t uDescIdx, uint32_t uLen)
196{
197 VIRTQ_USED_ELEM_T elem = { uDescIdx, uLen };
198 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
199 uint16_t const cQueueItems = RT_MAX(pVirtio->uQueueSize[idxQueue], 1); /* Make sure to avoid div-by-zero. */
200 PDMDevHlpPCIPhysWrite(pDevIns,
201 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF_DYN(VIRTQ_USED_T, aRing[usedIdx % cQueueItems]),
202 &elem, sizeof(elem));
203}
204
205DECLINLINE(void) virtioWriteUsedRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, uint16_t uIdx)
206{
207 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
208 PDMDevHlpPCIPhysWrite(pDevIns,
209 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF(VIRTQ_USED_T, uIdx),
210 &uIdx, sizeof(uIdx));
211}
212
213#ifdef LOG_ENABLED
214DECLINLINE(uint16_t) virtioReadUsedRingIdx(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
215{
216 uint16_t uIdx = 0;
217 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
218 PDMDevHlpPhysRead(pDevIns,
219 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF(VIRTQ_USED_T, uIdx),
220 &uIdx, sizeof(uIdx));
221 return uIdx;
222}
223#endif
224
225DECLINLINE(uint16_t) virtioReadUsedFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
226{
227 uint16_t fFlags = 0;
228 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
229 PDMDevHlpPhysRead(pDevIns,
230 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF(VIRTQ_USED_T, fFlags),
231 &fFlags, sizeof(fFlags));
232 return fFlags;
233}
234
235#if 0 /* unused */
236DECLINLINE(void) virtioWriteUsedFlags(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, uint32_t fFlags)
237{
238 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
239 RT_UNTRUSTED_VALIDATED_FENCE(); /* VirtIO 1.0, Section 3.2.1.4.1 */
240 PDMDevHlpPCIPhysWrite(pDevIns,
241 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF(VIRTQ_USED_T, fFlags),
242 &fFlags, sizeof(fFlags));
243}
244#endif
245
246#if 0 /* unused */
247DECLINLINE(uint16_t) virtioReadUsedAvailEvent(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
248{
249 uint16_t uAvailEventIdx;
250 RT_UNTRUSTED_VALIDATED_FENCE(); /* VirtIO 1.0, Section 3.2.1.4.1 */
251 /** VirtIO 1.0 uAvailEventIdx (avail_event) immediately follows ring */
252 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
253 PDMDevHlpPhysRead(pDevIns,
254 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF_DYN(VIRTQ_USED_T, aRing[pVirtio->uQueueSize[idxQueue]]),
255 &uAvailEventIdx, sizeof(uAvailEventIdx));
256 return uAvailEventIdx;
257}
258#endif
259
260#if 0 /* unused */
261DECLINLINE(void) virtioWriteUsedAvailEvent(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, uint32_t uAvailEventIdx)
262{
263 /** VirtIO 1.0 uAvailEventIdx (avail_event) immediately follows ring */
264 AssertMsg(pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK, ("Called with guest driver not ready\n"));
265 PDMDevHlpPCIPhysWrite(pDevIns,
266 pVirtio->aGCPhysQueueUsed[idxQueue] + RT_UOFFSETOF_DYN(VIRTQ_USED_T, aRing[pVirtio->uQueueSize[idxQueue]]),
267 &uAvailEventIdx, sizeof(uAvailEventIdx));
268}
269#endif
270
271/** @} */
272
273#ifdef LOG_ENABLED
274
275/**
276 * Does a formatted hex dump using Log(()), recommend using VIRTIO_HEX_DUMP() macro to
277 * control enabling of logging efficiently.
278 *
279 * @param pv pointer to buffer to dump contents of
280 * @param cb count of characters to dump from buffer
281 * @param uBase base address of per-row address prefixing of hex output
282 * @param pszTitle Optional title. If present displays title that lists
283 * provided text with value of cb to indicate size next to it.
284 */
285void virtioCoreHexDump(uint8_t *pv, uint32_t cb, uint32_t uBase, const char *pszTitle)
286{
287 if (pszTitle)
288 Log(("%s [%d bytes]:\n", pszTitle, cb));
289 for (uint32_t row = 0; row < RT_MAX(1, (cb / 16) + 1) && row * 16 < cb; row++)
290 {
291 Log(("%04x: ", row * 16 + uBase)); /* line address */
292 for (uint8_t col = 0; col < 16; col++)
293 {
294 uint32_t idx = row * 16 + col;
295 if (idx >= cb)
296 Log(("-- %s", (col + 1) % 8 ? "" : " "));
297 else
298 Log(("%02x %s", pv[idx], (col + 1) % 8 ? "" : " "));
299 }
300 for (uint32_t idx = row * 16; idx < row * 16 + 16; idx++)
301 Log(("%c", (idx >= cb) ? ' ' : (pv[idx] >= 0x20 && pv[idx] <= 0x7e ? pv[idx] : '.')));
302 Log(("\n"));
303 }
304 Log(("\n"));
305 RT_NOREF2(uBase, pv);
306}
307
308/**
309 * Log memory-mapped I/O input or output value.
310 *
311 * This is designed to be invoked by macros that can make contextual assumptions
312 * (e.g. implicitly derive MACRO parameters from the invoking function). It is exposed
313 * for the VirtIO client doing the device-specific implementation in order to log in a
314 * similar fashion accesses to the device-specific MMIO configuration structure. Macros
315 * that leverage this function are found in virtioCommonCfgAccessed() and can be
316 * used as an example of how to use this effectively for the device-specific
317 * code.
318 *
319 * @param pszFunc To avoid displaying this function's name via __FUNCTION__ or LogFunc()
320 * @param pszMember Name of struct member
321 * @param pv pointer to value
322 * @param cb size of value
323 * @param uOffset offset into member where value starts
324 * @param fWrite True if write I/O
325 * @param fHasIndex True if the member is indexed
326 * @param idx The index if fHasIndex
327 */
328void virtioCoreLogMappedIoValue(const char *pszFunc, const char *pszMember, uint32_t uMemberSize,
329 const void *pv, uint32_t cb, uint32_t uOffset, int fWrite,
330 int fHasIndex, uint32_t idx)
331{
332 if (!LogIs6Enabled())
333 return;
334
335 char szIdx[16];
336 if (fHasIndex)
337 RTStrPrintf(szIdx, sizeof(szIdx), "[%d]", idx);
338 else
339 szIdx[0] = '\0';
340
341 if (cb == 1 || cb == 2 || cb == 4 || cb == 8)
342 {
343 char szDepiction[64];
344 size_t cchDepiction;
345 if (uOffset != 0 || cb != uMemberSize) /* display bounds if partial member access */
346 cchDepiction = RTStrPrintf(szDepiction, sizeof(szDepiction), "%s%s[%d:%d]",
347 pszMember, szIdx, uOffset, uOffset + cb - 1);
348 else
349 cchDepiction = RTStrPrintf(szDepiction, sizeof(szDepiction), "%s%s", pszMember, szIdx);
350
351 /* padding */
352 if (cchDepiction < 30)
353 szDepiction[cchDepiction++] = ' ';
354 while (cchDepiction < 30)
355 szDepiction[cchDepiction++] = '.';
356 szDepiction[cchDepiction] = '\0';
357
358 RTUINT64U uValue;
359 uValue.u = 0;
360 memcpy(uValue.au8, pv, cb);
361 Log6Func(("%s: Guest %s %s %#0*RX64\n",
362 pszFunc, fWrite ? "wrote" : "read ", szDepiction, 2 + cb * 2, uValue.u));
363 }
364 else /* odd number or oversized access, ... log inline hex-dump style */
365 {
366 Log6Func(("%s: Guest %s %s%s[%d:%d]: %.*Rhxs\n",
367 pszFunc, fWrite ? "wrote" : "read ", pszMember,
368 szIdx, uOffset, uOffset + cb, cb, pv));
369 }
370 RT_NOREF2(fWrite, pszFunc);
371}
372
373#endif /* LOG_ENABLED */
374
375/**
376 * Makes the MMIO-mapped Virtio uDeviceStatus registers non-cryptic
377 */
378DECLINLINE(void) virtioLogDeviceStatus(uint8_t bStatus)
379{
380 if (bStatus == 0)
381 Log6(("RESET"));
382 else
383 {
384 int primed = 0;
385 if (bStatus & VIRTIO_STATUS_ACKNOWLEDGE)
386 Log6(("%sACKNOWLEDGE", primed++ ? "" : ""));
387 if (bStatus & VIRTIO_STATUS_DRIVER)
388 Log6(("%sDRIVER", primed++ ? " | " : ""));
389 if (bStatus & VIRTIO_STATUS_FEATURES_OK)
390 Log6(("%sFEATURES_OK", primed++ ? " | " : ""));
391 if (bStatus & VIRTIO_STATUS_DRIVER_OK)
392 Log6(("%sDRIVER_OK", primed++ ? " | " : ""));
393 if (bStatus & VIRTIO_STATUS_FAILED)
394 Log6(("%sFAILED", primed++ ? " | " : ""));
395 if (bStatus & VIRTIO_STATUS_DEVICE_NEEDS_RESET)
396 Log6(("%sNEEDS_RESET", primed++ ? " | " : ""));
397 (void)primed;
398 }
399}
400
401#ifdef IN_RING3
402/**
403 * Allocate client context for client to work with VirtIO-provided with queue
404 *
405 * @param pVirtio Pointer to the shared virtio state.
406 * @param idxQueue Queue number
407 * @param pcszName Name to give queue
408 *
409 * @returns VBox status code.
410 */
411int virtioCoreR3QueueAttach(PVIRTIOCORE pVirtio, uint16_t idxQueue, const char *pcszName)
412{
413 LogFunc(("%s\n", pcszName));
414 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
415 pVirtq->uAvailIdx = 0;
416 pVirtq->uUsedIdx = 0;
417 pVirtq->fEventThresholdReached = false;
418 RTStrCopy(pVirtq->szVirtqName, sizeof(pVirtq->szVirtqName), pcszName);
419 return VINF_SUCCESS;
420}
421#endif /* IN_RING3 */
422
423#if 0 /** @todo r=bird: no prototype or docs for this one */
424/**
425 * See API comments in header file for description
426 */
427int virtioQueueSkip(PVIRTIOCORE pVirtio, uint16_t idxQueue)
428{
429 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
430 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
431
432 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uQueueEnable[idxQueue],
433 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
434
435 if (virtioCoreQueueIsEmpty(pVirtio, idxQueue))
436 return VERR_NOT_AVAILABLE;
437
438 Log2Func(("%s avail_idx=%u\n", pVirtq->szVirtqName, pVirtq->uAvailIdx));
439 pVirtq->uAvailIdx++;
440
441 return VINF_SUCCESS;
442}
443#endif
444
445/**
446 * Check if the associated queue is empty
447 *
448 * @param pDevIns The device instance (for reading).
449 * @param pVirtio Pointer to the shared virtio state.
450 * @param idxQueue Queue number
451 *
452 * @retval true Queue is empty or unavailable.
453 * @retval false Queue is available and has entries
454 */
455bool virtioCoreQueueIsEmpty(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
456{
457 if (pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
458 return virtqIsEmpty(pDevIns, pVirtio, idxQueue);
459 return true;
460}
461
462#ifdef IN_RING3
463
464/**
465 * Removes descriptor chain from avail ring of indicated queue and converts the descriptor
466 * chain into its OUT (to device) and IN to guest components.
467 *
468 * Additionally it converts the OUT desc chain data to a contiguous virtual
469 * memory buffer for easy consumption by the caller. The caller must return the
470 * descriptor chain pointer via virtioCoreR3QueuePut() and then call virtioCoreQueueSync()
471 * at some point to return the data to the guest and complete the transaction.
472 *
473 * @param pDevIns The device instance.
474 * @param pVirtio Pointer to the shared virtio state.
475 * @param idxQueue Queue number
476 * @param fRemove flags whether to remove desc chain from queue (false = peek)
477 * @param ppDescChain Address to store pointer to descriptor chain that contains the
478 * pre-processed transaction information pulled from the virtq.
479 *
480 * @returns VBox status code:
481 * @retval VINF_SUCCESS Success
482 * @retval VERR_INVALID_STATE VirtIO not in ready state (asserted).
483 * @retval VERR_NOT_AVAILABLE If the queue is empty.
484 */
485int virtioCoreR3QueueGet(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue,
486 PPVIRTIO_DESC_CHAIN_T ppDescChain, bool fRemove)
487{
488 AssertReturn(ppDescChain, VERR_INVALID_PARAMETER);
489
490 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
491 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
492
493 PRTSGSEG paSegsIn = (PRTSGSEG)RTMemAlloc(VIRTQ_MAX_SIZE * sizeof(RTSGSEG));
494 AssertReturn(paSegsIn, VERR_NO_MEMORY);
495
496 PRTSGSEG paSegsOut = (PRTSGSEG)RTMemAlloc(VIRTQ_MAX_SIZE * sizeof(RTSGSEG));
497 AssertReturn(paSegsOut, VERR_NO_MEMORY);
498
499 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uQueueEnable[idxQueue],
500 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
501
502 if (virtqIsEmpty(pDevIns, pVirtio, idxQueue))
503 return VERR_NOT_AVAILABLE;
504
505 uint16_t uHeadIdx = virtioReadAvailDescIdx(pDevIns, pVirtio, idxQueue, pVirtq->uAvailIdx);
506 uint16_t uDescIdx = uHeadIdx;
507
508 Log3Func(("%s DESC CHAIN: (head) desc_idx=%u [avail_idx=%u]\n", pVirtq->szVirtqName, uHeadIdx, pVirtq->uAvailIdx));
509
510 if (fRemove)
511 pVirtq->uAvailIdx++;
512
513 VIRTQ_DESC_T desc;
514
515 uint32_t cbIn = 0, cbOut = 0, cSegsIn = 0, cSegsOut = 0;
516
517 do
518 {
519 RTSGSEG *pSeg;
520
521 /*
522 * Malicious guests may go beyond paSegsIn or paSegsOut boundaries by linking
523 * several descriptors into a loop. Since there is no legitimate way to get a sequences of
524 * linked descriptors exceeding the total number of descriptors in the ring (see @bugref{8620}),
525 * the following aborts I/O if breach and employs a simple log throttling algorithm to notify.
526 */
527 if (cSegsIn + cSegsOut >= VIRTQ_MAX_SIZE)
528 {
529 static volatile uint32_t s_cMessages = 0;
530 static volatile uint32_t s_cThreshold = 1;
531 if (ASMAtomicIncU32(&s_cMessages) == ASMAtomicReadU32(&s_cThreshold))
532 {
533 LogRelMax(64, ("Too many linked descriptors; check if the guest arranges descriptors in a loop.\n"));
534 if (ASMAtomicReadU32(&s_cMessages) != 1)
535 LogRelMax(64, ("(the above error has occured %u times so far)\n", ASMAtomicReadU32(&s_cMessages)));
536 ASMAtomicWriteU32(&s_cThreshold, ASMAtomicReadU32(&s_cThreshold) * 10);
537 }
538 break;
539 }
540 RT_UNTRUSTED_VALIDATED_FENCE();
541
542 virtioReadDesc(pDevIns, pVirtio, idxQueue, uDescIdx, &desc);
543
544 if (desc.fFlags & VIRTQ_DESC_F_WRITE)
545 {
546 Log3Func(("%s IN desc_idx=%u seg=%u addr=%RGp cb=%u\n", QUEUE_NAME(pVirtio, idxQueue), uDescIdx, cSegsIn, desc.GCPhysBuf, desc.cb));
547 cbIn += desc.cb;
548 pSeg = &(paSegsIn[cSegsIn++]);
549 }
550 else
551 {
552 Log3Func(("%s OUT desc_idx=%u seg=%u addr=%RGp cb=%u\n", QUEUE_NAME(pVirtio, idxQueue), uDescIdx, cSegsOut, desc.GCPhysBuf, desc.cb));
553 cbOut += desc.cb;
554 pSeg = &(paSegsOut[cSegsOut++]);
555 }
556
557 pSeg->pvSeg = (void *)desc.GCPhysBuf;
558 pSeg->cbSeg = desc.cb;
559
560 uDescIdx = desc.uDescIdxNext;
561 } while (desc.fFlags & VIRTQ_DESC_F_NEXT);
562
563 PRTSGBUF pSgPhysIn = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
564 AssertReturn(pSgPhysIn, VERR_NO_MEMORY);
565
566 RTSgBufInit(pSgPhysIn, (PCRTSGSEG)paSegsIn, cSegsIn);
567
568 PRTSGBUF pSgPhysOut = (PRTSGBUF)RTMemAllocZ(sizeof(RTSGBUF));
569 AssertReturn(pSgPhysOut, VERR_NO_MEMORY);
570
571 RTSgBufInit(pSgPhysOut, (PCRTSGSEG)paSegsOut, cSegsOut);
572
573 PVIRTIO_DESC_CHAIN_T pDescChain = (PVIRTIO_DESC_CHAIN_T)RTMemAllocZ(sizeof(VIRTIO_DESC_CHAIN_T));
574 AssertReturn(pDescChain, VERR_NO_MEMORY);
575
576 pDescChain->uHeadIdx = uHeadIdx;
577 pDescChain->cbPhysSend = cbOut;
578 pDescChain->pSgPhysSend = pSgPhysOut;
579 pDescChain->cbPhysReturn = cbIn;
580 pDescChain->pSgPhysReturn = pSgPhysIn;
581 *ppDescChain = pDescChain;
582
583 Log3Func(("%s -- segs OUT: %u (%u bytes) IN: %u (%u bytes) --\n", pVirtq->szVirtqName, cSegsOut, cbOut, cSegsIn, cbIn));
584
585 return VINF_SUCCESS;
586}
587
588/**
589 * Returns data to the guest to complete a transaction initiated by virtQueueGet().
590 *
591 * The caller passes in a pointer to a scatter-gather buffer of virtual memory segments
592 * and a pointer to the descriptor chain context originally derived from the pulled
593 * queue entry, and this function will write the virtual memory s/g buffer into the
594 * guest's physical memory free the descriptor chain. The caller handles the freeing
595 * (as needed) of the virtual memory buffer.
596 *
597 * @note This does a write-ahead to the used ring of the guest's queue. The data
598 * written won't be seen by the guest until the next call to virtioCoreQueueSync()
599 *
600 *
601 * @param pDevIns The device instance (for reading).
602 * @param pVirtio Pointer to the shared virtio state.
603 * @param idxQueue Queue number
604 *
605 * @param pSgVirtReturn Points toscatter-gather buffer of virtual memory
606 * segments the caller is returning to the guest.
607 *
608 * @param pDescChain This contains the context of the scatter-gather
609 * buffer originally pulled from the queue.
610 *
611 * @param fFence If true, put up copy fence (memory barrier) after
612 * copying to guest phys. mem.
613 *
614 * @returns VBox status code.
615 * @retval VINF_SUCCESS Success
616 * @retval VERR_INVALID_STATE VirtIO not in ready state
617 * @retval VERR_NOT_AVAILABLE Queue is empty
618 */
619int virtioCoreR3QueuePut(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, PRTSGBUF pSgVirtReturn,
620 PVIRTIO_DESC_CHAIN_T pDescChain, bool fFence)
621{
622 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
623 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
624 PRTSGBUF pSgPhysReturn = pDescChain->pSgPhysReturn;
625
626 AssertMsgReturn(IS_DRIVER_OK(pVirtio) /*&& pVirtio->uQueueEnable[idxQueue]*/,
627 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
628
629 Log3Func(("Copying client data to %s, desc chain (head desc_idx %d)\n",
630 QUEUE_NAME(pVirtio, idxQueue), virtioReadUsedRingIdx(pDevIns, pVirtio, idxQueue)));
631
632 /*
633 * Copy s/g buf (virtual memory) to guest phys mem (IN direction). This virtual memory
634 * block will be small (fixed portion of response header + sense buffer area or
635 * control commands or error return values)... The bulk of req data xfers to phys mem
636 * is handled by client */
637
638 size_t cbCopy = 0;
639 size_t cbRemain = RTSgBufCalcTotalLength(pSgVirtReturn);
640 RTSgBufReset(pSgPhysReturn); /* Reset ptr because req data may have already been written */
641 while (cbRemain)
642 {
643 PCRTSGSEG paSeg = &pSgPhysReturn->paSegs[pSgPhysReturn->idxSeg];
644 uint64_t dstSgStart = (uint64_t)paSeg->pvSeg;
645 uint64_t dstSgLen = (uint64_t)paSeg->cbSeg;
646 uint64_t dstSgCur = (uint64_t)pSgPhysReturn->pvSegCur;
647 cbCopy = RT_MIN((uint64_t)pSgVirtReturn->cbSegLeft, dstSgLen - (dstSgCur - dstSgStart));
648 PDMDevHlpPhysWrite(pDevIns, (RTGCPHYS)pSgPhysReturn->pvSegCur, pSgVirtReturn->pvSegCur, cbCopy);
649 RTSgBufAdvance(pSgVirtReturn, cbCopy);
650 RTSgBufAdvance(pSgPhysReturn, cbCopy);
651 cbRemain -= cbCopy;
652 }
653
654 if (fFence)
655 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); /* needed? */
656
657 /* If this write-ahead crosses threshold where the driver wants to get an event flag it */
658 if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
659 if (pVirtq->uUsedIdx == virtioReadAvailUsedEvent(pDevIns, pVirtio, idxQueue))
660 pVirtq->fEventThresholdReached = true;
661
662 Assert(!(cbCopy >> 32));
663
664 /*
665 * Place used buffer's descriptor in used ring but don't update used ring's slot index.
666 * That will be done with a subsequent client call to virtioCoreQueueSync() */
667 virtioWriteUsedElem(pDevIns, pVirtio, idxQueue, pVirtq->uUsedIdx++, pDescChain->uHeadIdx, (uint32_t)cbCopy);
668
669 Log2Func((".... Copied %zu bytes to %u byte buffer, residual=%zu\n",
670 cbCopy, pDescChain->cbPhysReturn, pDescChain->cbPhysReturn - cbCopy));
671
672 Log6Func(("Write ahead used_idx=%u, %s used_idx=%u\n",
673 pVirtq->uUsedIdx, QUEUE_NAME(pVirtio, idxQueue), virtioReadUsedRingIdx(pDevIns, pVirtio, idxQueue)));
674
675 RTMemFree((void *)pDescChain->pSgPhysSend->paSegs);
676 RTMemFree(pDescChain->pSgPhysSend);
677 RTMemFree((void *)pSgPhysReturn->paSegs);
678 RTMemFree(pSgPhysReturn);
679 RTMemFree(pDescChain);
680
681 return VINF_SUCCESS;
682}
683
684#endif /* IN_RING3 */
685
686/**
687 * Updates the indicated virtq's "used ring" descriptor index to match the
688 * current write-head index, thus exposing the data added to the used ring by all
689 * virtioCoreR3QueuePut() calls since the last sync. This should be called after one or
690 * more virtQueuePut() calls to inform the guest driver there is data in the queue.
691 * Explicit notifications (e.g. interrupt or MSI-X) will be sent to the guest,
692 * depending on VirtIO features negotiated and conditions, otherwise the guest
693 * will detect the update by polling. (see VirtIO 1.0
694 * specification, Section 2.4 "Virtqueues").
695 *
696 * @param pDevIns The device instance.
697 * @param pVirtio Pointer to the shared virtio state.
698 * @param idxQueue Queue number
699 *
700 * @returns VBox status code.
701 * @retval VINF_SUCCESS Success
702 * @retval VERR_INVALID_STATE VirtIO not in ready state
703 */
704int virtioCoreQueueSync(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue)
705{
706 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
707 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
708
709 AssertMsgReturn(IS_DRIVER_OK(pVirtio) && pVirtio->uQueueEnable[idxQueue],
710 ("Guest driver not in ready state.\n"), VERR_INVALID_STATE);
711
712 Log6Func(("Updating %s used_idx from %u to %u\n",
713 QUEUE_NAME(pVirtio, idxQueue), virtioReadUsedRingIdx(pDevIns, pVirtio, idxQueue), pVirtq->uUsedIdx));
714
715 virtioWriteUsedRingIdx(pDevIns, pVirtio, idxQueue, pVirtq->uUsedIdx);
716 virtioNotifyGuestDriver(pDevIns, pVirtio, idxQueue, false);
717
718 return VINF_SUCCESS;
719}
720
721#ifdef IN_RING3
722/**
723 */
724static void virtioR3QueueNotified(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint16_t idxQueue, uint16_t uNotifyIdx)
725{
726 /* See VirtIO 1.0, section 4.1.5.2 It implies that idxQueue and uNotifyIdx should match.
727 * Disregarding this notification may cause throughput to stop, however there's no way to know
728 * which was queue was intended for wake-up if the two parameters disagree. */
729
730 AssertMsg(uNotifyIdx == idxQueue,
731 ("Notification param disagreement. Guest kicked virtq %d's notify addr w/non-corresponding virtq idx %d\n",
732 idxQueue, uNotifyIdx));
733
734// AssertMsgReturn(uNotifyIdx == idxQueue,
735// ("Notification param disagreement. Guest kicked virtq %d's notify addr w/non-corresponding virtq idx %d\n",
736// idxQueue, uNotifyIdx));
737 RT_NOREF(uNotifyIdx);
738
739 AssertReturnVoid(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
740 Log6Func(("%s\n", pVirtio->virtqState[idxQueue].szVirtqName));
741
742 /* Inform client */
743 pVirtioCC->pfnQueueNotified(pVirtio, pVirtioCC, idxQueue);
744}
745#endif /* IN_RING3 */
746
747/**
748 * Trigger MSI-X or INT# interrupt to notify guest of data added to used ring of
749 * the specified virtq, depending on the interrupt configuration of the device
750 * and depending on negotiated and realtime constraints flagged by the guest driver.
751 *
752 * See VirtIO 1.0 specification (section 2.4.7).
753 *
754 * @param pDevIns The device instance.
755 * @param pVirtio Pointer to the shared virtio state.
756 * @param idxQueue Queue to check for guest interrupt handling preference
757 * @param fForce Overrides idxQueue, forcing notification regardless of driver's
758 * notification preferences. This is a safeguard to prevent
759 * stalls upon resuming the VM. VirtIO 1.0 specification Section 4.1.5.5
760 * indicates spurious interrupts are harmless to guest driver's state,
761 * as they only cause the guest driver to [re]scan queues for work to do.
762 */
763static void virtioNotifyGuestDriver(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t idxQueue, bool fForce)
764{
765 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
766 PVIRTQSTATE pVirtq = &pVirtio->virtqState[idxQueue];
767
768 AssertMsgReturnVoid(IS_DRIVER_OK(pVirtio), ("Guest driver not in ready state.\n"));
769 if (pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
770 {
771 if (pVirtq->fEventThresholdReached)
772 {
773 virtioKick(pDevIns, pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, pVirtio->uQueueMsixVector[idxQueue], fForce);
774 pVirtq->fEventThresholdReached = false;
775 return;
776 }
777 Log6Func(("...skipping interrupt: VIRTIO_F_EVENT_IDX set but threshold not reached\n"));
778 }
779 else
780 {
781 /** If guest driver hasn't suppressed interrupts, interrupt */
782 if (fForce || !(virtioReadUsedFlags(pDevIns, pVirtio, idxQueue) & VIRTQ_AVAIL_F_NO_INTERRUPT))
783 {
784 virtioKick(pDevIns, pVirtio, VIRTIO_ISR_VIRTQ_INTERRUPT, pVirtio->uQueueMsixVector[idxQueue], fForce);
785 return;
786 }
787 Log6Func(("...skipping interrupt. Guest flagged VIRTQ_AVAIL_F_NO_INTERRUPT for queue\n"));
788 }
789}
790
791/**
792 * Raise interrupt or MSI-X
793 *
794 * @param pDevIns The device instance.
795 * @param pVirtio Pointer to the shared virtio state.
796 * @param uCause Interrupt cause bit mask to set in PCI ISR port.
797 * @param uVec MSI-X vector, if enabled
798 * @param uForce True of out-of-band
799 */
800static int virtioKick(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint8_t uCause, uint16_t uMsixVector, bool fForce)
801{
802 if (fForce)
803 Log6Func(("reason: resumed after suspend\n"));
804 else
805 if (uCause == VIRTIO_ISR_VIRTQ_INTERRUPT)
806 Log6Func(("reason: buffer added to 'used' ring.\n"));
807 else
808 if (uCause == VIRTIO_ISR_DEVICE_CONFIG)
809 Log6Func(("reason: device config change\n"));
810
811 if (!pVirtio->fMsiSupport)
812 {
813 pVirtio->uISR |= uCause;
814 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
815 }
816 else if (uMsixVector != VIRTIO_MSI_NO_VECTOR)
817 {
818 Log6Func(("MSI-X enabled, calling PDMDevHlpPCISetIrq with vector: 0x%x\n", uMsixVector));
819 PDMDevHlpPCISetIrq(pDevIns, uMsixVector, 1);
820 }
821 return VINF_SUCCESS;
822}
823
824/**
825 * Lower interrupt. (Called when guest reads ISR)
826 *
827 * @param pDevIns The device instance.
828 */
829static void virtioLowerInterrupt(PPDMDEVINS pDevIns)
830{
831 /** @todo r=bird: MSI? */
832 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
833}
834
835static void virtioResetQueue(PVIRTIOCORE pVirtio, uint16_t idxQueue)
836{
837 Assert(idxQueue < RT_ELEMENTS(pVirtio->virtqState));
838 PVIRTQSTATE pVirtQ = &pVirtio->virtqState[idxQueue];
839 pVirtQ->uAvailIdx = 0;
840 pVirtQ->uUsedIdx = 0;
841 pVirtio->uQueueEnable[idxQueue] = false;
842 pVirtio->uQueueSize[idxQueue] = VIRTQ_MAX_SIZE;
843 pVirtio->uQueueNotifyOff[idxQueue] = idxQueue;
844
845 pVirtio->uQueueMsixVector[idxQueue] = idxQueue + 2;
846 if (!pVirtio->fMsiSupport) /* VirtIO 1.0, 4.1.4.3 and 4.1.5.1.2 */
847 pVirtio->uQueueMsixVector[idxQueue] = VIRTIO_MSI_NO_VECTOR;
848}
849
850static void virtioResetDevice(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio)
851{
852 Log2Func(("\n"));
853 pVirtio->uDeviceFeaturesSelect = 0;
854 pVirtio->uDriverFeaturesSelect = 0;
855 pVirtio->uConfigGeneration = 0;
856 pVirtio->uDeviceStatus = 0;
857 pVirtio->uISR = 0;
858
859 virtioLowerInterrupt(pDevIns);
860
861 if (!pVirtio->fMsiSupport) /* VirtIO 1.0, 4.1.4.3 and 4.1.5.1.2 */
862 pVirtio->uMsixConfig = VIRTIO_MSI_NO_VECTOR;
863
864 pVirtio->uNumQueues = VIRTQ_MAX_CNT;
865 for (uint16_t idxQueue = 0; idxQueue < pVirtio->uNumQueues; idxQueue++)
866 virtioResetQueue(pVirtio, idxQueue);
867}
868
869#if 0 /** @todo r=bird: Probably not needed. */
870/**
871 * Enable or disable queue
872 *
873 * @param pVirtio Pointer to the shared virtio state.
874 * @param idxQueue Queue number
875 * @param fEnabled Flag indicating whether to enable queue or not
876 */
877void virtioCoreQueueEnable(PVIRTIOCORE pVirtio, uint16_t idxQueue, bool fEnabled)
878{
879 if (fEnabled)
880 pVirtio->uQueueSize[idxQueue] = VIRTQ_MAX_SIZE;
881 else
882 pVirtio->uQueueSize[idxQueue] = 0;
883}
884#endif
885
886#if 0 /** @todo r=bird: This isn't invoked by anyone. Why? */
887/**
888 * Initiate orderly reset procedure.
889 * Invoked by client to reset the device and driver (see VirtIO 1.0 section 2.1.1/2.1.2)
890 */
891void virtioCoreResetAll(PVIRTIOCORE pVirtio)
892{
893 LogFunc(("VIRTIO RESET REQUESTED!!!\n"));
894 pVirtio->uDeviceStatus |= VIRTIO_STATUS_DEVICE_NEEDS_RESET;
895 if (pVirtio->uDeviceStatus & VIRTIO_STATUS_DRIVER_OK)
896 {
897 pVirtio->fGenUpdatePending = true;
898 virtioKick(pVirtio, VIRTIO_ISR_DEVICE_CONFIG, pVirtio->uMsixConfig, false /* fForce */);
899 }
900}
901#endif
902
903#ifdef IN_RING3
904/**
905 * Invoked by this implementation when guest driver resets the device.
906 * The driver itself will not until the device has read the status change.
907 */
908static void virtioGuestR3WasReset(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC)
909{
910 LogFunc(("Guest reset the device\n"));
911
912 /* Let the client know */
913 pVirtioCC->pfnStatusChanged(pVirtio, pVirtioCC, 0);
914 virtioResetDevice(pDevIns, pVirtio);
915}
916#endif /* IN_RING3 */
917
918/**
919 * Handle accesses to Common Configuration capability
920 *
921 * @returns VBox status code
922 *
923 * @param pDevIns The device instance.
924 * @param pVirtio Pointer to the shared virtio state.
925 * @param pVirtioCC Pointer to the current context virtio state.
926 * @param fWrite Set if write access, clear if read access.
927 * @param offCfg The common configuration capability offset.
928 * @param cb Number of bytes to read or write
929 * @param pv Pointer to location to write to or read from
930 */
931static int virtioCommonCfgAccessed(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC,
932 int fWrite, uint32_t offCfg, unsigned cb, void *pv)
933{
934/**
935 * This macro resolves to boolean true if the implied parameters, offCfg and cb,
936 * match the field offset and size of a field in the Common Cfg struct, (or if
937 * it is a 64-bit field, if it accesses either 32-bit part as a 32-bit access)
938 * This is mandated by section 4.1.3.1 of the VirtIO 1.0 specification)
939 *
940 * @param member Member of VIRTIO_PCI_COMMON_CFG_T
941 * @param offCfg Implied parameter: Offset into VIRTIO_PCI_COMMON_CFG_T
942 * @param cb Implied parameter: Number of bytes to access
943 * @result true or false
944 */
945#define MATCH_COMMON_CFG(member) \
946 ( ( RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member) == 8 \
947 && ( offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) \
948 || offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) + sizeof(uint32_t)) \
949 && cb == sizeof(uint32_t)) \
950 || ( offCfg == RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member) \
951 && cb == RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member)) )
952
953#ifdef LOG_ENABLED
954# define LOG_COMMON_CFG_ACCESS(member, a_offIntra) \
955 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member), \
956 pv, cb, a_offIntra, fWrite, false, 0);
957# define LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, a_offIntra) \
958 virtioCoreLogMappedIoValue(__FUNCTION__, #member, RT_SIZEOFMEMB(VIRTIO_PCI_COMMON_CFG_T, member), \
959 pv, cb, a_offIntra, fWrite, true, idx);
960#else
961# define LOG_COMMON_CFG_ACCESS(member, a_offIntra) do { } while (0)
962# define LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, a_offIntra) do { } while (0)
963#endif
964
965#define COMMON_CFG_ACCESSOR(member) \
966 do \
967 { \
968 uint32_t offIntra = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
969 if (fWrite) \
970 memcpy((char *)&pVirtio->member + offIntra, (const char *)pv, cb); \
971 else \
972 memcpy(pv, (const char *)&pVirtio->member + offIntra, cb); \
973 LOG_COMMON_CFG_ACCESS(member, offIntra); \
974 } while(0)
975
976#define COMMON_CFG_ACCESSOR_INDEXED(member, idx) \
977 do \
978 { \
979 uint32_t offIntra = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
980 if (fWrite) \
981 memcpy((char *)&pVirtio->member[idx] + offIntra, pv, cb); \
982 else \
983 memcpy(pv, (const char *)&pVirtio->member[idx] + offIntra, cb); \
984 LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, offIntra); \
985 } while(0)
986
987#define COMMON_CFG_ACCESSOR_READONLY(member) \
988 do \
989 { \
990 uint32_t offIntra = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
991 if (fWrite) \
992 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s\n", #member)); \
993 else \
994 { \
995 memcpy(pv, (const char *)&pVirtio->member + offIntra, cb); \
996 LOG_COMMON_CFG_ACCESS(member, offIntra); \
997 } \
998 } while(0)
999
1000#define COMMON_CFG_ACCESSOR_INDEXED_READONLY(member, idx) \
1001 do \
1002 { \
1003 uint32_t offIntra = offCfg - RT_OFFSETOF(VIRTIO_PCI_COMMON_CFG_T, member); \
1004 if (fWrite) \
1005 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.%s[%d]\n", #member, idx)); \
1006 else \
1007 { \
1008 memcpy(pv, (char const *)&pVirtio->member[idx] + offIntra, cb); \
1009 LOG_COMMON_CFG_ACCESS_INDEXED(member, idx, offIntra); \
1010 } \
1011 } while(0)
1012
1013
1014 int rc = VINF_SUCCESS;
1015 uint64_t val;
1016 if (MATCH_COMMON_CFG(uDeviceFeatures))
1017 {
1018 if (fWrite) /* Guest WRITE pCommonCfg>uDeviceFeatures */
1019 {
1020 LogFunc(("Guest attempted to write readonly virtio_pci_common_cfg.device_feature\n"));
1021 return VINF_SUCCESS;
1022 }
1023 else /* Guest READ pCommonCfg->uDeviceFeatures */
1024 {
1025 switch (pVirtio->uDeviceFeaturesSelect)
1026 {
1027 case 0:
1028 val = pVirtio->uDeviceFeatures & UINT32_C(0xffffffff);
1029 memcpy(pv, &val, cb);
1030 LOG_COMMON_CFG_ACCESS(uDeviceFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDeviceFeatures));
1031 break;
1032 case 1:
1033 val = pVirtio->uDeviceFeatures >> 32;
1034 memcpy(pv, &val, cb);
1035 LOG_COMMON_CFG_ACCESS(uDeviceFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDeviceFeatures) + 4);
1036 break;
1037 default:
1038 LogFunc(("Guest read uDeviceFeatures with out of range selector (%#x), returning 0\n",
1039 pVirtio->uDeviceFeaturesSelect));
1040 return VINF_IOM_MMIO_UNUSED_00;
1041 }
1042 }
1043 }
1044 else if (MATCH_COMMON_CFG(uDriverFeatures))
1045 {
1046 if (fWrite) /* Guest WRITE pCommonCfg->udriverFeatures */
1047 {
1048 switch (pVirtio->uDriverFeaturesSelect)
1049 {
1050 case 0:
1051 memcpy(&pVirtio->uDriverFeatures, pv, cb);
1052 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures));
1053 break;
1054 case 1:
1055 memcpy((char *)&pVirtio->uDriverFeatures + sizeof(uint32_t), pv, cb);
1056 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures) + 4);
1057 break;
1058 default:
1059 LogFunc(("Guest wrote uDriverFeatures with out of range selector (%#x), returning 0\n",
1060 pVirtio->uDriverFeaturesSelect));
1061 return VINF_SUCCESS;
1062 }
1063 }
1064 else /* Guest READ pCommonCfg->udriverFeatures */
1065 {
1066 switch (pVirtio->uDriverFeaturesSelect)
1067 {
1068 case 0:
1069 val = pVirtio->uDriverFeatures & 0xffffffff;
1070 memcpy(pv, &val, cb);
1071 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures));
1072 break;
1073 case 1:
1074 val = (pVirtio->uDriverFeatures >> 32) & 0xffffffff;
1075 memcpy(pv, &val, cb);
1076 LOG_COMMON_CFG_ACCESS(uDriverFeatures, offCfg - RT_UOFFSETOF(VIRTIO_PCI_COMMON_CFG_T, uDriverFeatures) + 4);
1077 break;
1078 default:
1079 LogFunc(("Guest read uDriverFeatures with out of range selector (%#x), returning 0\n",
1080 pVirtio->uDriverFeaturesSelect));
1081 return VINF_IOM_MMIO_UNUSED_00;
1082 }
1083 }
1084 }
1085 else if (MATCH_COMMON_CFG(uNumQueues))
1086 {
1087 if (fWrite)
1088 {
1089 Log2Func(("Guest attempted to write readonly virtio_pci_common_cfg.num_queues\n"));
1090 return VINF_SUCCESS;
1091 }
1092 else
1093 {
1094 *(uint16_t *)pv = VIRTQ_MAX_CNT;
1095 LOG_COMMON_CFG_ACCESS(uNumQueues, 0);
1096 }
1097 }
1098 else if (MATCH_COMMON_CFG(uDeviceStatus))
1099 {
1100 if (fWrite) /* Guest WRITE pCommonCfg->uDeviceStatus */
1101 {
1102 uint8_t const fNewStatus = *(uint8_t *)pv;
1103 Log6Func(("Guest wrote uDeviceStatus (%#x, was %#x, change #%x) ................ (",
1104 fNewStatus, pVirtio->uDeviceStatus, fNewStatus ^ pVirtio->uDeviceStatus));
1105 virtioLogDeviceStatus(fNewStatus);
1106 Log6((")\n"));
1107
1108 /* If the status changed or we were reset, we need to go to ring-3 as
1109 it requires notifying the parent device. */
1110 bool const fStatusChanged = (fNewStatus & VIRTIO_STATUS_DRIVER_OK)
1111 != (pVirtio->uPrevDeviceStatus & VIRTIO_STATUS_DRIVER_OK);
1112#ifndef IN_RING3
1113 if (fStatusChanged || fNewStatus == 0)
1114 {
1115 Log6Func(("=>ring3\n"));
1116 return VINF_IOM_R3_MMIO_WRITE;
1117 }
1118#endif
1119 pVirtio->uDeviceStatus = fNewStatus;
1120
1121#ifdef IN_RING3
1122 /*
1123 * Notify client only if status actually changed from last time and when we're reset.
1124 */
1125 if (pVirtio->uDeviceStatus == 0)
1126 virtioGuestR3WasReset(pDevIns, pVirtio, pVirtioCC);
1127 if (fStatusChanged)
1128 pVirtioCC->pfnStatusChanged(pVirtio, pVirtioCC, fNewStatus & VIRTIO_STATUS_DRIVER_OK);
1129#endif
1130 /*
1131 * Save the current status for the next write so we can see what changed.
1132 */
1133 pVirtio->uPrevDeviceStatus = pVirtio->uDeviceStatus;
1134 }
1135 else /* Guest READ pCommonCfg->uDeviceStatus */
1136 {
1137 Log6Func(("Guest read uDeviceStatus ................ ("));
1138 *(uint32_t *)pv = pVirtio->uDeviceStatus; /** @todo r=bird: Why 32-bit write here, the field is 8-bit? */
1139 virtioLogDeviceStatus(pVirtio->uDeviceStatus);
1140 Log6((")\n"));
1141 }
1142 }
1143 else
1144 if (MATCH_COMMON_CFG(uMsixConfig))
1145 COMMON_CFG_ACCESSOR(uMsixConfig);
1146 else
1147 if (MATCH_COMMON_CFG(uDeviceFeaturesSelect))
1148 COMMON_CFG_ACCESSOR(uDeviceFeaturesSelect);
1149 else
1150 if (MATCH_COMMON_CFG(uDriverFeaturesSelect))
1151 COMMON_CFG_ACCESSOR(uDriverFeaturesSelect);
1152 else
1153 if (MATCH_COMMON_CFG(uConfigGeneration))
1154 COMMON_CFG_ACCESSOR_READONLY(uConfigGeneration);
1155 else
1156 if (MATCH_COMMON_CFG(uQueueSelect))
1157 COMMON_CFG_ACCESSOR(uQueueSelect);
1158 else
1159 if (MATCH_COMMON_CFG(uQueueSize))
1160 COMMON_CFG_ACCESSOR_INDEXED(uQueueSize, pVirtio->uQueueSelect);
1161 else
1162 if (MATCH_COMMON_CFG(uQueueMsixVector))
1163 COMMON_CFG_ACCESSOR_INDEXED(uQueueMsixVector, pVirtio->uQueueSelect);
1164 else
1165 if (MATCH_COMMON_CFG(uQueueEnable))
1166 COMMON_CFG_ACCESSOR_INDEXED(uQueueEnable, pVirtio->uQueueSelect);
1167 else
1168 if (MATCH_COMMON_CFG(uQueueNotifyOff))
1169 COMMON_CFG_ACCESSOR_INDEXED_READONLY(uQueueNotifyOff, pVirtio->uQueueSelect);
1170 else
1171 if (MATCH_COMMON_CFG(aGCPhysQueueDesc))
1172 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysQueueDesc, pVirtio->uQueueSelect);
1173 else
1174 if (MATCH_COMMON_CFG(aGCPhysQueueAvail))
1175 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysQueueAvail, pVirtio->uQueueSelect);
1176 else
1177 if (MATCH_COMMON_CFG(aGCPhysQueueUsed))
1178 COMMON_CFG_ACCESSOR_INDEXED(aGCPhysQueueUsed, pVirtio->uQueueSelect);
1179 else
1180 {
1181 Log2Func(("Bad guest %s access to virtio_pci_common_cfg: offCfg=%#x (%d), cb=%d\n",
1182 fWrite ? "write" : "read ", offCfg, offCfg, cb));
1183 return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
1184 }
1185
1186#undef COMMON_CFG_ACCESSOR_READONLY
1187#undef COMMON_CFG_ACCESSOR_INDEXED_READONLY
1188#undef COMMON_CFG_ACCESSOR_INDEXED
1189#undef COMMON_CFG_ACCESSOR
1190#undef LOG_COMMON_CFG_ACCESS_INDEXED
1191#undef LOG_COMMON_CFG_ACCESS
1192#undef MATCH_COMMON_CFG
1193#ifndef IN_RING3
1194 RT_NOREF(pDevIns, pVirtioCC);
1195#endif
1196 return rc;
1197}
1198
1199/**
1200 * @callback_method_impl{FNIOMMMIONEWREAD,
1201 * Memory mapped I/O Handler for PCI Capabilities read operations.}
1202 */
1203static DECLCALLBACK(VBOXSTRICTRC) virtioMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1204{
1205 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1206 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1207 Assert(pVirtio == (PVIRTIOCORE)pvUser); RT_NOREF(pvUser);
1208
1209 /** @todo r=bird: This code does not handle reads spanning more than one
1210 * capability structure/area. How does that match the spec? For instance
1211 * if the guest uses a 64-bit MOV instruction on this MMIO region, you'll
1212 * see cb=8 here. Same if it uses 16 or 32 byte reads. Intel allows all
1213 * this, so question is how it's supposed to be handled. At a minimum there
1214 * must be an explanation of that here.
1215 */
1216
1217 uint32_t offIntra;
1218 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocDeviceCap))
1219 {
1220#ifdef IN_RING3
1221 /*
1222 * Callback to client to manage device-specific configuration.
1223 */
1224 VBOXSTRICTRC rcStrict = pVirtioCC->pfnDevCapRead(pDevIns, offIntra, pv, cb);
1225
1226 /*
1227 * Additionally, anytime any part of the device-specific configuration (which our client maintains)
1228 * is READ it needs to be checked to see if it changed since the last time any part was read, in
1229 * order to maintain the config generation (see VirtIO 1.0 spec, section 4.1.4.3.1)
1230 */
1231 bool fDevSpecificFieldChanged = !!memcmp(pVirtioCC->pbDevSpecificCfg + offIntra,
1232 pVirtioCC->pbPrevDevSpecificCfg + offIntra,
1233 RT_MIN(cb, pVirtioCC->cbDevSpecificCfg - offIntra));
1234
1235 memcpy(pVirtioCC->pbPrevDevSpecificCfg, pVirtioCC->pbDevSpecificCfg, pVirtioCC->cbDevSpecificCfg);
1236
1237 if (pVirtio->fGenUpdatePending || fDevSpecificFieldChanged)
1238 {
1239 ++pVirtio->uConfigGeneration;
1240 Log6Func(("Bumped cfg. generation to %d because %s%s\n",
1241 pVirtio->uConfigGeneration,
1242 fDevSpecificFieldChanged ? "<dev cfg changed> " : "",
1243 pVirtio->fGenUpdatePending ? "<update was pending>" : ""));
1244 pVirtio->fGenUpdatePending = false;
1245 }
1246 return rcStrict;
1247#else
1248 return VINF_IOM_R3_MMIO_READ;
1249#endif
1250 }
1251
1252 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocCommonCfgCap))
1253 return virtioCommonCfgAccessed(pDevIns, pVirtio, pVirtioCC, false /* fWrite */, offIntra, cb, pv);
1254
1255 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocIsrCap) && cb == sizeof(uint8_t))
1256 {
1257 *(uint8_t *)pv = pVirtio->uISR;
1258 Log6Func(("Read and clear ISR\n"));
1259 pVirtio->uISR = 0; /* VirtIO specification requires reads of ISR to clear it */
1260 virtioLowerInterrupt(pDevIns);
1261 return VINF_SUCCESS;
1262 }
1263
1264 ASSERT_GUEST_MSG_FAILED(("Bad read access to mapped capabilities region: off=%RGp cb=%u\n", off, cb));
1265 return VINF_IOM_MMIO_UNUSED_00;
1266}
1267
1268/**
1269 * @callback_method_impl{FNIOMMMIONEWREAD,
1270 * Memory mapped I/O Handler for PCI Capabilities write operations.}
1271 */
1272static DECLCALLBACK(VBOXSTRICTRC) virtioMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1273{
1274 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1275 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1276 Assert(pVirtio == (PVIRTIOCORE)pvUser); RT_NOREF(pvUser);
1277
1278 /** @todo r=bird: This code does not handle writes spanning more than one
1279 * capability structure/area. How does that match the spec? For instance
1280 * if the guest uses a 64-bit MOV instruction on this MMIO region, you'll
1281 * see cb=8 here. Same if it uses 16 or 32 byte reads. Intel allows all
1282 * this, so question is how it's supposed to be handled. At a minimum there
1283 * must be an explanation of that here.
1284 */
1285
1286 uint32_t offIntra;
1287 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocDeviceCap))
1288 {
1289#ifdef IN_RING3
1290 /*
1291 * Pass this MMIO write access back to the client to handle
1292 */
1293 return pVirtioCC->pfnDevCapWrite(pDevIns, offIntra, pv, cb);
1294#else
1295 return VINF_IOM_R3_MMIO_WRITE;
1296#endif
1297 }
1298
1299 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocCommonCfgCap))
1300 return virtioCommonCfgAccessed(pDevIns, pVirtio, pVirtioCC, true /* fWrite */, offIntra, cb, (void *)pv);
1301
1302 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocIsrCap) && cb == sizeof(uint8_t))
1303 {
1304 pVirtio->uISR = *(uint8_t *)pv;
1305 Log6Func(("Setting uISR = 0x%02x (virtq interrupt: %d, dev confg interrupt: %d)\n",
1306 pVirtio->uISR & 0xff,
1307 pVirtio->uISR & VIRTIO_ISR_VIRTQ_INTERRUPT,
1308 RT_BOOL(pVirtio->uISR & VIRTIO_ISR_DEVICE_CONFIG)));
1309 return VINF_SUCCESS;
1310 }
1311
1312 /* This *should* be guest driver dropping index of a new descriptor in avail ring */
1313 if (MATCHES_VIRTIO_CAP_STRUCT(off, cb, offIntra, pVirtio->LocNotifyCap) && cb == sizeof(uint16_t))
1314 {
1315#ifdef IN_RING3
1316 virtioR3QueueNotified(pVirtio, pVirtioCC, offIntra / VIRTIO_NOTIFY_OFFSET_MULTIPLIER, *(uint16_t *)pv);
1317 return VINF_SUCCESS;
1318#else
1319 return VINF_IOM_R3_MMIO_WRITE;
1320#endif
1321 }
1322
1323 ASSERT_GUEST_MSG_FAILED(("Bad write access to mapped capabilities region: off=%RGp pv=%#p{%.*Rhxs} cb=%u\n", off, pv, cb, pv, cb));
1324 return VINF_SUCCESS;
1325}
1326
1327#ifdef IN_RING3
1328
1329/**
1330 * @callback_method_impl{FNPCICONFIGREAD}
1331 */
1332static DECLCALLBACK(VBOXSTRICTRC) virtioR3PciConfigRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
1333 uint32_t uAddress, unsigned cb, uint32_t *pu32Value)
1334{
1335 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1336 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1337 RT_NOREF(pPciDev);
1338
1339 LogFlowFunc(("pDevIns=%p pPciDev=%p uAddress=%#x cb=%u pu32Value=%p\n",
1340 pDevIns, pPciDev, uAddress, cb, pu32Value));
1341 if (uAddress == pVirtio->uPciCfgDataOff)
1342 {
1343 /*
1344 * VirtIO 1.0 spec section 4.1.4.7 describes a required alternative access capability
1345 * whereby the guest driver can specify a bar, offset, and length via the PCI configuration space
1346 * (the virtio_pci_cfg_cap capability), and access data items.
1347 */
1348 uint32_t uLength = pVirtioCC->pPciCfgCap->pciCap.uLength;
1349 uint32_t uOffset = pVirtioCC->pPciCfgCap->pciCap.uOffset;
1350 uint8_t uBar = pVirtioCC->pPciCfgCap->pciCap.uBar;
1351
1352 if ( (uLength != 1 && uLength != 2 && uLength != 4)
1353 || cb != uLength
1354 || uBar != VIRTIO_REGION_PCI_CAP)
1355 {
1356 ASSERT_GUEST_MSG_FAILED(("Guest read virtio_pci_cfg_cap.pci_cfg_data using mismatching config. Ignoring\n"));
1357 *pu32Value = UINT32_MAX;
1358 return VINF_SUCCESS;
1359 }
1360
1361 VBOXSTRICTRC rcStrict = virtioMmioRead(pDevIns, pVirtio, uOffset, pu32Value, cb);
1362 Log2Func(("virtio: Guest read virtio_pci_cfg_cap.pci_cfg_data, bar=%d, offset=%d, length=%d, result=%d -> %Rrc\n",
1363 uBar, uOffset, uLength, *pu32Value, VBOXSTRICTRC_VAL(rcStrict)));
1364 return rcStrict;
1365 }
1366 return VINF_PDM_PCI_DO_DEFAULT;
1367}
1368
1369/**
1370 * @callback_method_impl{FNPCICONFIGWRITE}
1371 */
1372static DECLCALLBACK(VBOXSTRICTRC) virtioR3PciConfigWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
1373 uint32_t uAddress, unsigned cb, uint32_t u32Value)
1374{
1375 PVIRTIOCORE pVirtio = PDMINS_2_DATA(pDevIns, PVIRTIOCORE);
1376 PVIRTIOCORECC pVirtioCC = PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC);
1377 RT_NOREF(pPciDev);
1378
1379 LogFlowFunc(("pDevIns=%p pPciDev=%p uAddress=%#x cb=%u u32Value=%#x\n", pDevIns, pPciDev, uAddress, cb, u32Value));
1380 if (uAddress == pVirtio->uPciCfgDataOff)
1381 {
1382 /* VirtIO 1.0 spec section 4.1.4.7 describes a required alternative access capability
1383 * whereby the guest driver can specify a bar, offset, and length via the PCI configuration space
1384 * (the virtio_pci_cfg_cap capability), and access data items. */
1385
1386 uint32_t uLength = pVirtioCC->pPciCfgCap->pciCap.uLength;
1387 uint32_t uOffset = pVirtioCC->pPciCfgCap->pciCap.uOffset;
1388 uint8_t uBar = pVirtioCC->pPciCfgCap->pciCap.uBar;
1389
1390 if ( (uLength != 1 && uLength != 2 && uLength != 4)
1391 || cb != uLength
1392 || uBar != VIRTIO_REGION_PCI_CAP)
1393 {
1394 ASSERT_GUEST_MSG_FAILED(("Guest write virtio_pci_cfg_cap.pci_cfg_data using mismatching config. Ignoring\n"));
1395 return VINF_SUCCESS;
1396 }
1397
1398 VBOXSTRICTRC rcStrict = virtioMmioWrite(pDevIns, pVirtio, uOffset, &u32Value, cb);
1399 Log2Func(("Guest wrote virtio_pci_cfg_cap.pci_cfg_data, bar=%d, offset=%x, length=%x, value=%d -> %Rrc\n",
1400 uBar, uOffset, uLength, u32Value, VBOXSTRICTRC_VAL(rcStrict)));
1401 return rcStrict;
1402 }
1403 return VINF_PDM_PCI_DO_DEFAULT;
1404}
1405
1406
1407/*********************************************************************************************************************************
1408* Saved state. *
1409*********************************************************************************************************************************/
1410
1411/**
1412 * Called from the FNSSMDEVSAVEEXEC function of the device.
1413 *
1414 * @param pVirtio Pointer to the shared virtio state.
1415 * @param pHlp The ring-3 device helpers.
1416 * @param pSSM The saved state handle.
1417 * @returns VBox status code.
1418 */
1419int virtioCoreR3SaveExec(PVIRTIOCORE pVirtio, PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
1420{
1421 pHlp->pfnSSMPutU64(pSSM, VIRTIO_SAVEDSTATE_MARKER);
1422 pHlp->pfnSSMPutU32(pSSM, VIRTIO_SAVEDSTATE_VERSION);
1423
1424 pHlp->pfnSSMPutBool(pSSM, pVirtio->fGenUpdatePending);
1425 pHlp->pfnSSMPutU8(pSSM, pVirtio->uDeviceStatus);
1426 pHlp->pfnSSMPutU8(pSSM, pVirtio->uConfigGeneration);
1427 pHlp->pfnSSMPutU8(pSSM, pVirtio->uPciCfgDataOff);
1428 pHlp->pfnSSMPutU8(pSSM, pVirtio->uISR);
1429 pHlp->pfnSSMPutU16(pSSM, pVirtio->uQueueSelect);
1430 pHlp->pfnSSMPutU32(pSSM, pVirtio->uDeviceFeaturesSelect);
1431 pHlp->pfnSSMPutU32(pSSM, pVirtio->uDriverFeaturesSelect);
1432 pHlp->pfnSSMPutU64(pSSM, pVirtio->uDriverFeatures);
1433 Assert(pVirtio->uNumQueues == VIRTQ_MAX_CNT); /** @todo r=bird: See todo in struct & virtioCoreR3LoadExec. */
1434 pHlp->pfnSSMPutU32(pSSM, pVirtio->uNumQueues);
1435
1436 for (uint32_t i = 0; i < pVirtio->uNumQueues; i++)
1437 {
1438 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysQueueDesc[i]);
1439 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysQueueAvail[i]);
1440 pHlp->pfnSSMPutGCPhys64(pSSM, pVirtio->aGCPhysQueueUsed[i]);
1441 pHlp->pfnSSMPutU16(pSSM, pVirtio->uQueueNotifyOff[i]);
1442 pHlp->pfnSSMPutU16(pSSM, pVirtio->uQueueMsixVector[i]);
1443 pHlp->pfnSSMPutU16(pSSM, pVirtio->uQueueEnable[i]);
1444 pHlp->pfnSSMPutU16(pSSM, pVirtio->uQueueSize[i]);
1445 pHlp->pfnSSMPutU16(pSSM, pVirtio->virtqState[i].uAvailIdx);
1446 pHlp->pfnSSMPutU16(pSSM, pVirtio->virtqState[i].uUsedIdx);
1447 int rc = pHlp->pfnSSMPutMem(pSSM, pVirtio->virtqState[i].szVirtqName, 32);
1448 AssertRCReturn(rc, rc);
1449 }
1450
1451 return VINF_SUCCESS;
1452}
1453
1454/**
1455 * Called from the FNSSMDEVLOADEXEC function of the device.
1456 *
1457 * @param pVirtio Pointer to the shared virtio state.
1458 * @param pHlp The ring-3 device helpers.
1459 * @param pSSM The saved state handle.
1460 * @returns VBox status code.
1461 */
1462int virtioCoreR3LoadExec(PVIRTIOCORE pVirtio, PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
1463{
1464 /*
1465 * Check the marker and (embedded) version number.
1466 */
1467 uint64_t uMarker = 0;
1468 int rc = pHlp->pfnSSMGetU64(pSSM, &uMarker);
1469 AssertRCReturn(rc, rc);
1470 if (uMarker != VIRTIO_SAVEDSTATE_MARKER)
1471 return pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
1472 N_("Expected marker value %#RX64 found %#RX64 instead"),
1473 VIRTIO_SAVEDSTATE_MARKER, uMarker);
1474 uint32_t uVersion = 0;
1475 rc = pHlp->pfnSSMGetU32(pSSM, &uVersion);
1476 AssertRCReturn(rc, rc);
1477 if (uVersion != VIRTIO_SAVEDSTATE_VERSION)
1478 return pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
1479 N_("Unsupported virtio version: %u"), uVersion);
1480
1481 /*
1482 * Load the state.
1483 */
1484 pHlp->pfnSSMGetBool(pSSM, &pVirtio->fGenUpdatePending);
1485 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uDeviceStatus);
1486 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uConfigGeneration);
1487 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uPciCfgDataOff);
1488 pHlp->pfnSSMGetU8(pSSM, &pVirtio->uISR);
1489 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uQueueSelect);
1490 pHlp->pfnSSMGetU32(pSSM, &pVirtio->uDeviceFeaturesSelect);
1491 pHlp->pfnSSMGetU32(pSSM, &pVirtio->uDriverFeaturesSelect);
1492 pHlp->pfnSSMGetU64(pSSM, &pVirtio->uDriverFeatures);
1493
1494 /* Make sure the queue count is within expectations. */
1495 /** @todo r=bird: Turns out the expectations are exactly VIRTQ_MAX_CNT, bug? */
1496 rc = pHlp->pfnSSMGetU32(pSSM, &pVirtio->uNumQueues);
1497 AssertRCReturn(rc, rc);
1498 AssertReturn(pVirtio->uNumQueues == VIRTQ_MAX_CNT,
1499 pHlp->pfnSSMSetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
1500 N_("Saved queue count %u, expected %u"), uVersion, VIRTQ_MAX_CNT));
1501 AssertCompile(RT_ELEMENTS(pVirtio->virtqState) == VIRTQ_MAX_CNT);
1502 AssertCompile(RT_ELEMENTS(pVirtio->aGCPhysQueueDesc) == VIRTQ_MAX_CNT);
1503
1504 for (uint32_t idxQueue = 0; idxQueue < pVirtio->uNumQueues; idxQueue++)
1505 {
1506 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysQueueDesc[idxQueue]);
1507 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysQueueAvail[idxQueue]);
1508 pHlp->pfnSSMGetGCPhys64(pSSM, &pVirtio->aGCPhysQueueUsed[idxQueue]);
1509 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uQueueNotifyOff[idxQueue]);
1510 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uQueueMsixVector[idxQueue]);
1511 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uQueueEnable[idxQueue]);
1512 pHlp->pfnSSMGetU16(pSSM, &pVirtio->uQueueSize[idxQueue]);
1513 pHlp->pfnSSMGetU16(pSSM, &pVirtio->virtqState[idxQueue].uAvailIdx);
1514 pHlp->pfnSSMGetU16(pSSM, &pVirtio->virtqState[idxQueue].uUsedIdx);
1515 rc = pHlp->pfnSSMGetMem(pSSM, pVirtio->virtqState[idxQueue].szVirtqName,
1516 sizeof(pVirtio->virtqState[idxQueue].szVirtqName));
1517 AssertRCReturn(rc, rc);
1518 }
1519
1520 return VINF_SUCCESS;
1521}
1522
1523
1524/*********************************************************************************************************************************
1525* Device Level *
1526*********************************************************************************************************************************/
1527
1528/**
1529 * This should be called from PDMDEVREGR3::pfnReset.
1530 *
1531 * @param pDevIns The device instance.
1532 * @param pVirtio Pointer to the shared virtio state.
1533 */
1534void virtioCoreR3PropagateResetNotification(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio)
1535{
1536 /** @todo r=bird: You probably need to do something here. See
1537 * virtioScsiR3Reset. */
1538 RT_NOREF(pDevIns, pVirtio);
1539}
1540
1541
1542/**
1543 * This sends notification ('kicks') guest driver to check queues for any new
1544 * elements in the used queue to process.
1545 *
1546 * It should be called after resuming in case anything was added to the queues
1547 * during suspend/quiescing and a notification was missed, to prevent the guest
1548 * from stalling after suspend.
1549 */
1550void virtioCoreR3PropagateResumeNotification(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio)
1551{
1552 virtioNotifyGuestDriver(pDevIns, pVirtio, 0 /* idxQueue */, true /* fForce */);
1553}
1554
1555
1556/**
1557 * This should be called from PDMDEVREGR3::pfnDestruct.
1558 *
1559 * @param pDevIns The device instance.
1560 * @param pVirtio Pointer to the shared virtio state.
1561 * @param pVirtioCC Pointer to the ring-3 virtio state.
1562 */
1563void virtioCoreR3Term(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC)
1564{
1565 if (pVirtioCC->pbPrevDevSpecificCfg)
1566 {
1567 RTMemFree(pVirtioCC->pbPrevDevSpecificCfg);
1568 pVirtioCC->pbPrevDevSpecificCfg = NULL;
1569 }
1570 RT_NOREF(pDevIns, pVirtio);
1571}
1572
1573
1574/**
1575 * Setup PCI device controller and Virtio state
1576 *
1577 * This should be called from PDMDEVREGR3::pfnConstruct.
1578 *
1579 * @param pDevIns The device instance.
1580 * @param pVirtio Pointer to the shared virtio state. This
1581 * must be the first member in the shared
1582 * device instance data!
1583 * @param pVirtioCC Pointer to the ring-3 virtio state. This
1584 * must be the first member in the ring-3
1585 * device instance data!
1586 * @param pPciParams Values to populate industry standard PCI Configuration Space data structure
1587 * @param pcszInstance Device instance name (format-specifier)
1588 * @param fDevSpecificFeatures VirtIO device-specific features offered by
1589 * client
1590 * @param cbDevSpecificCfg Size of virtio_pci_device_cap device-specific struct
1591 * @param pvDevSpecificCfg Address of client's dev-specific
1592 * configuration struct.
1593 */
1594int virtioCoreR3Init(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, PVIRTIOPCIPARAMS pPciParams,
1595 const char *pcszInstance, uint64_t fDevSpecificFeatures, void *pvDevSpecificCfg, uint16_t cbDevSpecificCfg)
1596{
1597 /*
1598 * The pVirtio state must be the first member of the shared device instance
1599 * data, otherwise we cannot get our bearings in the PCI configuration callbacks.
1600 */
1601 AssertLogRelReturn(pVirtio == PDMINS_2_DATA(pDevIns, PVIRTIOCORE), VERR_STATE_CHANGED);
1602 AssertLogRelReturn(pVirtioCC == PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC), VERR_STATE_CHANGED);
1603
1604 /*
1605 * Caller must initialize these.
1606 */
1607 AssertReturn(pVirtioCC->pfnStatusChanged, VERR_INVALID_POINTER);
1608 AssertReturn(pVirtioCC->pfnQueueNotified, VERR_INVALID_POINTER);
1609 AssertReturn(pVirtioCC->pfnDevCapRead, VERR_INVALID_POINTER);
1610 AssertReturn(pVirtioCC->pfnDevCapWrite, VERR_INVALID_POINTER);
1611
1612#if 0 /* Until pdmR3DvHlp_PCISetIrq() impl is fixed and Assert that limits vec to 0 is removed */
1613# ifdef VBOX_WITH_MSI_DEVICES
1614 pVirtio->fMsiSupport = true;
1615# endif
1616#endif
1617
1618 /*
1619 * The host features offered include both device-specific features
1620 * and reserved feature bits (device independent)
1621 */
1622 pVirtio->uDeviceFeatures = VIRTIO_F_VERSION_1
1623 | VIRTIO_DEV_INDEPENDENT_FEATURES_OFFERED
1624 | fDevSpecificFeatures;
1625
1626 RTStrCopy(pVirtio->szInstance, sizeof(pVirtio->szInstance), pcszInstance);
1627
1628 pVirtio->uDeviceStatus = 0;
1629 pVirtioCC->cbDevSpecificCfg = cbDevSpecificCfg;
1630 pVirtioCC->pbDevSpecificCfg = (uint8_t *)pvDevSpecificCfg;
1631 pVirtioCC->pbPrevDevSpecificCfg = (uint8_t *)RTMemDup(pvDevSpecificCfg, cbDevSpecificCfg);
1632 AssertLogRelReturn(pVirtioCC->pbPrevDevSpecificCfg, VERR_NO_MEMORY);
1633
1634 /* Set PCI config registers (assume 32-bit mode) */
1635 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
1636 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
1637
1638 PDMPciDevSetRevisionId(pPciDev, DEVICE_PCI_REVISION_ID_VIRTIO);
1639 PDMPciDevSetVendorId(pPciDev, DEVICE_PCI_VENDOR_ID_VIRTIO);
1640 PDMPciDevSetSubSystemVendorId(pPciDev, DEVICE_PCI_VENDOR_ID_VIRTIO);
1641 PDMPciDevSetDeviceId(pPciDev, pPciParams->uDeviceId);
1642 PDMPciDevSetClassBase(pPciDev, pPciParams->uClassBase);
1643 PDMPciDevSetClassSub(pPciDev, pPciParams->uClassSub);
1644 PDMPciDevSetClassProg(pPciDev, pPciParams->uClassProg);
1645 PDMPciDevSetSubSystemId(pPciDev, pPciParams->uSubsystemId);
1646 PDMPciDevSetInterruptLine(pPciDev, pPciParams->uInterruptLine);
1647 PDMPciDevSetInterruptPin(pPciDev, pPciParams->uInterruptPin);
1648
1649 /* Register PCI device */
1650 int rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
1651 if (RT_FAILURE(rc))
1652 return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio: cannot register PCI Device")); /* can we put params in this error? */
1653
1654 rc = PDMDevHlpPCIInterceptConfigAccesses(pDevIns, pPciDev, virtioR3PciConfigRead, virtioR3PciConfigWrite);
1655 AssertRCReturn(rc, rc);
1656
1657
1658 /* Construct & map PCI vendor-specific capabilities for virtio host negotiation with guest driver */
1659
1660 /* The following capability mapped via VirtIO 1.0: struct virtio_pci_cfg_cap (VIRTIO_PCI_CFG_CAP_T)
1661 * as a mandatory but suboptimal alternative interface to host device capabilities, facilitating
1662 * access the memory of any BAR. If the guest uses it (the VirtIO driver on Linux doesn't),
1663 * Unlike Common, Notify, ISR and Device capabilities, it is accessed directly via PCI Config region.
1664 * therefore does not contribute to the capabilities region (BAR) the other capabilities use.
1665 */
1666#define CFG_ADDR_2_IDX(addr) ((uint8_t)(((uintptr_t)(addr) - (uintptr_t)&pPciDev->abConfig[0])))
1667#define SET_PCI_CAP_LOC(a_pPciDev, a_pCfg, a_LocCap, a_uMmioLengthAlign) \
1668 do { \
1669 (a_LocCap).offMmio = (a_pCfg)->uOffset; \
1670 (a_LocCap).cbMmio = RT_ALIGN_T((a_pCfg)->uLength, a_uMmioLengthAlign, uint16_t); \
1671 (a_LocCap).offPci = (uint16_t)(uintptr_t)((uint8_t *)(a_pCfg) - &(a_pPciDev)->abConfig[0]); \
1672 (a_LocCap).cbPci = (a_pCfg)->uCapLen; \
1673 } while (0)
1674
1675 PVIRTIO_PCI_CAP_T pCfg;
1676 uint32_t cbRegion = 0;
1677
1678 /* Common capability (VirtIO 1.0 spec, section 4.1.4.3) */
1679 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[0x40];
1680 pCfg->uCfgType = VIRTIO_PCI_CAP_COMMON_CFG;
1681 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1682 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
1683 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
1684 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1685 pCfg->uOffset = RT_ALIGN_32(0, 4); /* reminder, in case someone changes offset */
1686 pCfg->uLength = sizeof(VIRTIO_PCI_COMMON_CFG_T);
1687 cbRegion += pCfg->uLength;
1688 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocCommonCfgCap, 2);
1689 pVirtioCC->pCommonCfgCap = pCfg;
1690
1691 /*
1692 * Notify capability (VirtIO 1.0 spec, section 4.1.4.4). Note: uLength is based the choice
1693 * of this implementation that each queue's uQueueNotifyOff is set equal to (QueueSelect) ordinal
1694 * value of the queue */
1695 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
1696 pCfg->uCfgType = VIRTIO_PCI_CAP_NOTIFY_CFG;
1697 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1698 pCfg->uCapLen = sizeof(VIRTIO_PCI_NOTIFY_CAP_T);
1699 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
1700 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1701 pCfg->uOffset = pVirtioCC->pCommonCfgCap->uOffset + pVirtioCC->pCommonCfgCap->uLength;
1702 pCfg->uOffset = RT_ALIGN_32(pCfg->uOffset, 2); /** @todo r=bird: Why is this word aligned rather than dword? If there is a
1703 * theoretical chance we won't allways be on a dword boundrary here, the
1704 * read/write really will need to handle cross capability reads. */
1705 pCfg->uLength = VIRTQ_MAX_CNT * VIRTIO_NOTIFY_OFFSET_MULTIPLIER + 2; /* will change in VirtIO 1.1 */
1706 cbRegion += pCfg->uLength;
1707 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocNotifyCap, 1);
1708 pVirtioCC->pNotifyCap = (PVIRTIO_PCI_NOTIFY_CAP_T)pCfg;
1709 pVirtioCC->pNotifyCap->uNotifyOffMultiplier = VIRTIO_NOTIFY_OFFSET_MULTIPLIER;
1710
1711 /* ISR capability (VirtIO 1.0 spec, section 4.1.4.5)
1712 *
1713 * VirtIO 1.0 spec says 8-bit, unaligned in MMIO space. Example/diagram
1714 * of spec shows it as a 32-bit field with upper bits 'reserved'
1715 * Will take spec words more literally than the diagram for now.
1716 */
1717 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
1718 pCfg->uCfgType = VIRTIO_PCI_CAP_ISR_CFG;
1719 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1720 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
1721 pCfg->uCapNext = CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen;
1722 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1723 pCfg->uOffset = pVirtioCC->pNotifyCap->pciCap.uOffset + pVirtioCC->pNotifyCap->pciCap.uLength; /** @todo r=bird: This probably is _not_ dword aligned, given that the previous structure is 0x32 (50) bytes long. */
1724 pCfg->uLength = sizeof(uint8_t);
1725 cbRegion += pCfg->uLength;
1726 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocIsrCap, 4);
1727 pVirtioCC->pIsrCap = pCfg;
1728
1729 /* PCI Cfg capability (VirtIO 1.0 spec, section 4.1.4.7)
1730 * This capability doesn't get page-MMIO mapped. Instead uBar, uOffset and uLength are intercepted
1731 * by trapping PCI configuration I/O and get modulated by consumers to locate fetch and read/write
1732 * values from any region. NOTE: The linux driver not only doesn't use this feature, it will not
1733 * even list it as present if uLength isn't non-zero and 4-byte-aligned as the linux driver is
1734 * initializing. */
1735
1736 pVirtio->uPciCfgDataOff = pCfg->uCapNext + RT_OFFSETOF(VIRTIO_PCI_CFG_CAP_T, uPciCfgData);
1737 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
1738 pCfg->uCfgType = VIRTIO_PCI_CAP_PCI_CFG;
1739 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1740 pCfg->uCapLen = sizeof(VIRTIO_PCI_CFG_CAP_T);
1741 pCfg->uCapNext = (pVirtio->fMsiSupport || pVirtioCC->pbDevSpecificCfg) ? CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen : 0;
1742 pCfg->uBar = 0;
1743 pCfg->uOffset = 0;
1744 pCfg->uLength = 0;
1745 cbRegion += pCfg->uLength;
1746 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocPciCfgCap, 1);
1747 pVirtioCC->pPciCfgCap = (PVIRTIO_PCI_CFG_CAP_T)pCfg;
1748
1749 if (pVirtioCC->pbDevSpecificCfg)
1750 {
1751 /* Following capability (via VirtIO 1.0, section 4.1.4.6). Client defines the
1752 * device-specific config fields struct and passes size to this constructor */
1753 pCfg = (PVIRTIO_PCI_CAP_T)&pPciDev->abConfig[pCfg->uCapNext];
1754 pCfg->uCfgType = VIRTIO_PCI_CAP_DEVICE_CFG;
1755 pCfg->uCapVndr = VIRTIO_PCI_CAP_ID_VENDOR;
1756 pCfg->uCapLen = sizeof(VIRTIO_PCI_CAP_T);
1757 pCfg->uCapNext = pVirtio->fMsiSupport ? CFG_ADDR_2_IDX(pCfg) + pCfg->uCapLen : 0;
1758 pCfg->uBar = VIRTIO_REGION_PCI_CAP;
1759 pCfg->uOffset = pVirtioCC->pIsrCap->uOffset + pVirtioCC->pIsrCap->uLength;
1760 pCfg->uOffset = RT_ALIGN_32(pCfg->uOffset, 4);
1761 pCfg->uLength = cbDevSpecificCfg;
1762 cbRegion += pCfg->uLength;
1763 SET_PCI_CAP_LOC(pPciDev, pCfg, pVirtio->LocDeviceCap, 4);
1764 pVirtioCC->pDeviceCap = pCfg;
1765 }
1766 else
1767 Assert(pVirtio->LocDeviceCap.cbMmio == 0 && pVirtio->LocDeviceCap.cbPci == 0);
1768
1769 if (pVirtio->fMsiSupport)
1770 {
1771 PDMMSIREG aMsiReg;
1772 RT_ZERO(aMsiReg);
1773 aMsiReg.iMsixCapOffset = pCfg->uCapNext;
1774 aMsiReg.iMsixNextOffset = 0;
1775 aMsiReg.iMsixBar = VIRTIO_REGION_MSIX_CAP;
1776 aMsiReg.cMsixVectors = VBOX_MSIX_MAX_ENTRIES;
1777 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg); /* see MsixR3init() */
1778 if (RT_FAILURE(rc))
1779 {
1780 /* See PDMDevHlp.cpp:pdmR3DevHlp_PCIRegisterMsi */
1781 LogFunc(("Failed to configure MSI-X (%Rrc). Reverting to INTx\n", rc));
1782 pVirtio->fMsiSupport = false;
1783 }
1784 else
1785 Log2Func(("Using MSI-X for guest driver notification\n"));
1786 }
1787 else
1788 LogFunc(("MSI-X not available for VBox, using INTx notification\n"));
1789
1790
1791 /* Set offset to first capability and enable PCI dev capabilities */
1792 PDMPciDevSetCapabilityList(pPciDev, 0x40);
1793 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
1794
1795 /* Linux drivers/virtio/virtio_pci_modern.c tries to map at least a page for the
1796 * 'unknown' device-specific capability without querying the capability to figure
1797 * out size, so pad with an extra page */
1798
1799 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, VIRTIO_REGION_PCI_CAP, RT_ALIGN_32(cbRegion + PAGE_SIZE, PAGE_SIZE),
1800 PCI_ADDRESS_SPACE_MEM, virtioMmioWrite, virtioMmioRead, pVirtio,
1801 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, "virtio-scsi MMIO",
1802 &pVirtio->hMmioPciCap);
1803 AssertLogRelRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio: cannot register PCI Capabilities address space")));
1804
1805 return rc;
1806}
1807
1808#else /* !IN_RING3 */
1809
1810/**
1811 * Sets up the core ring-0/raw-mode virtio bits.
1812 *
1813 * @returns VBox status code.
1814 * @param pDevIns The device instance.
1815 * @param pVirtio Pointer to the shared virtio state. This must be the first
1816 * member in the shared device instance data!
1817 * @param pVirtioCC Pointer to the current context virtio state. This must be the
1818 * first member in the currenct context's device instance data!
1819 */
1820int virtioCoreRZInit(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC)
1821{
1822 AssertLogRelReturn(pVirtio == PDMINS_2_DATA(pDevIns, PVIRTIOCORE), VERR_STATE_CHANGED);
1823 AssertLogRelReturn(pVirtioCC == PDMINS_2_DATA_CC(pDevIns, PVIRTIOCORECC), VERR_STATE_CHANGED);
1824
1825 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pVirtio->hMmioPciCap, virtioMmioWrite, virtioMmioRead, pVirtio);
1826 AssertRCReturn(rc, rc);
1827 return rc;
1828}
1829
1830#endif /* !IN_RING3 */
1831
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