VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAllIommu.cpp@ 93582

Last change on this file since 93582 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/* $Id: PDMAllIommu.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * PDM IOMMU - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2021-2022 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_PDM
23#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
24#include "PDMInternal.h"
25
26#include <VBox/vmm/vmcc.h>
27#include <iprt/string.h>
28#ifdef IN_RING3
29# include <iprt/mem.h>
30#endif
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36/**
37 * Gets the PDM IOMMU for the current context from the PDM device instance.
38 */
39#ifdef IN_RING0
40#define PDMDEVINS_TO_IOMMU(a_pDevIns) &(a_pDevIns)->Internal.s.pGVM->pdmr0.s.aIommus[0];
41#else
42#define PDMDEVINS_TO_IOMMU(a_pDevIns) &(a_pDevIns)->Internal.s.pVMR3->pdm.s.aIommus[0];
43#endif
44
45
46/**
47 * Gets the PCI device ID (Bus:Dev:Fn) for the given PCI device.
48 *
49 * @returns PCI device ID.
50 * @param pDevIns The device instance.
51 * @param pPciDev The PCI device structure. Cannot be NULL.
52 */
53DECL_FORCE_INLINE(uint16_t) pdmIommuGetPciDeviceId(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev)
54{
55 uint8_t const idxBus = pPciDev->Int.s.idxPdmBus;
56#if defined(IN_RING0)
57 PGVM pGVM = pDevIns->Internal.s.pGVM;
58 Assert(idxBus < RT_ELEMENTS(pGVM->pdmr0.s.aPciBuses));
59 PCPDMPCIBUSR0 pBus = &pGVM->pdmr0.s.aPciBuses[idxBus];
60#elif defined(IN_RING3)
61 PVM pVM = pDevIns->Internal.s.pVMR3;
62 Assert(idxBus < RT_ELEMENTS(pVM->pdm.s.aPciBuses));
63 PCPDMPCIBUS pBus = &pVM->pdm.s.aPciBuses[idxBus];
64#endif
65 return PCIBDF_MAKE(pBus->iBus, pPciDev->uDevFn);
66}
67
68
69/**
70 * Returns whether an IOMMU instance is present.
71 *
72 * @returns @c true if an IOMMU is present, @c false otherwise.
73 * @param pDevIns The device instance.
74 */
75bool pdmIommuIsPresent(PPDMDEVINS pDevIns)
76{
77#ifdef IN_RING0
78 PCPDMIOMMUR3 pIommuR3 = &pDevIns->Internal.s.pGVM->pdm.s.aIommus[0];
79#else
80 PCPDMIOMMUR3 pIommuR3 = &pDevIns->Internal.s.pVMR3->pdm.s.aIommus[0];
81#endif
82 return pIommuR3->pDevInsR3 != NULL;
83}
84
85
86/** @copydoc PDMIOMMUREGR3::pfnMsiRemap */
87int pdmIommuMsiRemap(PPDMDEVINS pDevIns, uint16_t idDevice, PCMSIMSG pMsiIn, PMSIMSG pMsiOut)
88{
89 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
90 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
91 Assert(pDevInsIommu);
92 if (pDevInsIommu != pDevIns)
93 return pIommu->pfnMsiRemap(pDevInsIommu, idDevice, pMsiIn, pMsiOut);
94 return VERR_IOMMU_CANNOT_CALL_SELF;
95}
96
97
98/**
99 * Bus master physical memory read after translating the physical address using the
100 * IOMMU.
101 *
102 * @returns VBox status code.
103 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
104 *
105 * @param pDevIns The device instance.
106 * @param pPciDev The PCI device. Cannot be NULL.
107 * @param GCPhys The guest-physical address to read.
108 * @param pvBuf Where to put the data read.
109 * @param cbRead How many bytes to read.
110 * @param fFlags Combination of PDM_DEVHLP_PHYS_RW_F_XXX.
111 *
112 * @thread Any.
113 */
114int pdmIommuMemAccessRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead, uint32_t fFlags)
115{
116 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
117 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
118 if (pDevInsIommu)
119 {
120 if (pDevInsIommu != pDevIns)
121 { /* likely */ }
122 else
123 return VERR_IOMMU_CANNOT_CALL_SELF;
124
125 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
126 int rc = VINF_SUCCESS;
127 while (cbRead > 0)
128 {
129 RTGCPHYS GCPhysOut;
130 size_t cbContig;
131 rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys, cbRead, PDMIOMMU_MEM_F_READ, &GCPhysOut, &cbContig);
132 if (RT_SUCCESS(rc))
133 {
134 Assert(cbContig > 0 && cbContig <= cbRead);
135 /** @todo Handle strict return codes from PGMPhysRead. */
136 rc = pDevIns->CTX_SUFF(pHlp)->pfnPhysRead(pDevIns, GCPhysOut, pvBuf, cbContig, fFlags);
137 if (RT_SUCCESS(rc))
138 {
139 cbRead -= cbContig;
140 pvBuf = (void *)((uintptr_t)pvBuf + cbContig);
141 GCPhys += cbContig;
142 }
143 else
144 break;
145 }
146 else
147 {
148 LogFunc(("IOMMU memory read failed. idDevice=%#x GCPhys=%#RGp cb=%zu rc=%Rrc\n", idDevice, GCPhys, cbRead, rc));
149
150 /*
151 * We should initialize the read buffer on failure for devices that don't check
152 * return codes (but would verify the data). But we still want to propagate the
153 * error code from the IOMMU to the device, see @bugref{9936#c3}.
154 */
155 memset(pvBuf, 0xff, cbRead);
156 break;
157 }
158 }
159 return rc;
160 }
161 return VERR_IOMMU_NOT_PRESENT;
162}
163
164
165/**
166 * Bus master physical memory write after translating the physical address using the
167 * IOMMU.
168 *
169 * @returns VBox status code.
170 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
171 *
172 * @param pDevIns The device instance.
173 * @param pPciDev The PCI device structure. Cannot be NULL.
174 * @param GCPhys The guest-physical address to write.
175 * @param pvBuf The data to write.
176 * @param cbWrite How many bytes to write.
177 * @param fFlags Combination of PDM_DEVHLP_PHYS_RW_F_XXX.
178 *
179 * @thread Any.
180 */
181int pdmIommuMemAccessWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite,
182 uint32_t fFlags)
183{
184 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
185 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
186 if (pDevInsIommu)
187 {
188 if (pDevInsIommu != pDevIns)
189 { /* likely */ }
190 else
191 return VERR_IOMMU_CANNOT_CALL_SELF;
192
193 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
194 int rc = VINF_SUCCESS;
195 while (cbWrite > 0)
196 {
197 RTGCPHYS GCPhysOut;
198 size_t cbContig;
199 rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys, cbWrite, PDMIOMMU_MEM_F_WRITE, &GCPhysOut, &cbContig);
200 if (RT_SUCCESS(rc))
201 {
202 Assert(cbContig > 0 && cbContig <= cbWrite);
203 /** @todo Handle strict return codes from PGMPhysWrite. */
204 rc = pDevIns->CTX_SUFF(pHlp)->pfnPhysWrite(pDevIns, GCPhysOut, pvBuf, cbContig, fFlags);
205 if (RT_SUCCESS(rc))
206 {
207 cbWrite -= cbContig;
208 pvBuf = (const void *)((uintptr_t)pvBuf + cbContig);
209 GCPhys += cbContig;
210 }
211 else
212 break;
213 }
214 else
215 {
216 LogFunc(("IOMMU memory write failed. idDevice=%#x GCPhys=%#RGp cb=%zu rc=%Rrc\n", idDevice, GCPhys, cbWrite, rc));
217 break;
218 }
219 }
220 return rc;
221 }
222 return VERR_IOMMU_NOT_PRESENT;
223}
224
225
226#ifdef IN_RING3
227/**
228 * Requests the mapping of a guest page into ring-3 in preparation for a bus master
229 * physical memory read operation.
230 *
231 * Refer pfnPhysGCPhys2CCPtrReadOnly() for further details.
232 *
233 * @returns VBox status code.
234 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
235 *
236 * @param pDevIns The device instance.
237 * @param pPciDev The PCI device structure. Cannot be NULL.
238 * @param GCPhys The guest physical address of the page that should be
239 * mapped.
240 * @param fFlags Flags reserved for future use, MBZ.
241 * @param ppv Where to store the address corresponding to GCPhys.
242 * @param pLock Where to store the lock information that
243 * pfnPhysReleasePageMappingLock needs.
244 */
245int pdmR3IommuMemAccessReadCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, uint32_t fFlags, void const **ppv,
246 PPGMPAGEMAPLOCK pLock)
247{
248 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
249 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
250 if (pDevInsIommu)
251 {
252 if (pDevInsIommu != pDevIns)
253 { /* likely */ }
254 else
255 return VERR_IOMMU_CANNOT_CALL_SELF;
256
257 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
258 size_t cbContig = 0;
259 RTGCPHYS GCPhysOut = NIL_RTGCPHYS;
260 int rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys & X86_PAGE_BASE_MASK, X86_PAGE_SIZE, PDMIOMMU_MEM_F_READ,
261 &GCPhysOut, &cbContig);
262 if (RT_SUCCESS(rc))
263 {
264 Assert(GCPhysOut != NIL_RTGCPHYS);
265 Assert(cbContig == X86_PAGE_SIZE);
266 return pDevIns->pHlpR3->pfnPhysGCPhys2CCPtrReadOnly(pDevIns, GCPhysOut, fFlags, ppv, pLock);
267 }
268
269 LogFunc(("IOMMU memory read for pointer access failed. idDevice=%#x GCPhys=%#RGp rc=%Rrc\n", idDevice, GCPhys, rc));
270 return rc;
271 }
272 return VERR_IOMMU_NOT_PRESENT;
273}
274
275
276/**
277 * Requests the mapping of a guest page into ring-3 in preparation for a bus master
278 * physical memory write operation.
279 *
280 * Refer pfnPhysGCPhys2CCPtr() for further details.
281 *
282 * @returns VBox status code.
283 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
284 *
285 * @param pDevIns The device instance.
286 * @param pPciDev The PCI device structure. Cannot be NULL.
287 * @param GCPhys The guest physical address of the page that should be
288 * mapped.
289 * @param fFlags Flags reserved for future use, MBZ.
290 * @param ppv Where to store the address corresponding to GCPhys.
291 * @param pLock Where to store the lock information that
292 * pfnPhysReleasePageMappingLock needs.
293 */
294int pdmR3IommuMemAccessWriteCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, uint32_t fFlags, void **ppv,
295 PPGMPAGEMAPLOCK pLock)
296{
297 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
298 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
299 if (pDevInsIommu)
300 {
301 if (pDevInsIommu != pDevIns)
302 { /* likely */ }
303 else
304 return VERR_IOMMU_CANNOT_CALL_SELF;
305
306 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
307 size_t cbContig = 0;
308 RTGCPHYS GCPhysOut = NIL_RTGCPHYS;
309 int rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys & X86_PAGE_BASE_MASK, X86_PAGE_SIZE, PDMIOMMU_MEM_F_WRITE,
310 &GCPhysOut, &cbContig);
311 if (RT_SUCCESS(rc))
312 {
313 Assert(GCPhysOut != NIL_RTGCPHYS);
314 Assert(cbContig == X86_PAGE_SIZE);
315 return pDevIns->pHlpR3->pfnPhysGCPhys2CCPtr(pDevIns, GCPhysOut, fFlags, ppv, pLock);
316 }
317
318 LogFunc(("IOMMU memory write for pointer access failed. idDevice=%#x GCPhys=%#RGp rc=%Rrc\n", idDevice, GCPhys, rc));
319 return rc;
320 }
321 return VERR_IOMMU_NOT_PRESENT;
322}
323
324
325/**
326 * Requests the mapping of multiple guest pages into ring-3 in prepartion for a bus
327 * master physical memory read operation.
328 *
329 * Refer pfnPhysBulkGCPhys2CCPtrReadOnly() for further details.
330 *
331 * @returns VBox status code.
332 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
333 *
334 * @param pDevIns The device instance.
335 * @param pPciDev The PCI device structure. Cannot be NULL.
336 * @param cPages Number of pages to lock.
337 * @param paGCPhysPages The guest physical address of the pages that
338 * should be mapped (@a cPages entries).
339 * @param fFlags Flags reserved for future use, MBZ.
340 * @param papvPages Where to store the ring-3 mapping addresses
341 * corresponding to @a paGCPhysPages.
342 * @param paLocks Where to store the locking information that
343 * pfnPhysBulkReleasePageMappingLock needs (@a cPages
344 * in length).
345 */
346int pdmR3IommuMemAccessBulkReadCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t cPages, PCRTGCPHYS paGCPhysPages,
347 uint32_t fFlags, const void **papvPages, PPGMPAGEMAPLOCK paLocks)
348{
349 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
350 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
351 if (pDevInsIommu)
352 {
353 if (pDevInsIommu != pDevIns)
354 { /* likely */ }
355 else
356 return VERR_IOMMU_CANNOT_CALL_SELF;
357
358 /* Allocate space for translated addresses. */
359 size_t const cbIovas = cPages * sizeof(uint64_t);
360 PRTGCPHYS paGCPhysOut = (PRTGCPHYS)RTMemAllocZ(cbIovas);
361 if (paGCPhysOut)
362 { /* likely */ }
363 else
364 {
365 LogFunc(("caller='%s'/%d: returns %Rrc - Failed to alloc %zu bytes for IOVA addresses\n",
366 pDevIns->pReg->szName, pDevIns->iInstance, VERR_NO_MEMORY, cbIovas));
367 return VERR_NO_MEMORY;
368 }
369
370 /* Ask the IOMMU for corresponding translated physical addresses. */
371 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
372 AssertCompile(sizeof(RTGCPHYS) == sizeof(uint64_t));
373 int rc = pIommu->pfnMemBulkAccess(pDevInsIommu, idDevice, cPages, (uint64_t const *)paGCPhysPages, PDMIOMMU_MEM_F_READ,
374 paGCPhysOut);
375 if (RT_SUCCESS(rc))
376 {
377 /* Perform the bulk mapping but with the translated addresses. */
378 rc = pDevIns->pHlpR3->pfnPhysBulkGCPhys2CCPtrReadOnly(pDevIns, cPages, paGCPhysOut, fFlags, papvPages, paLocks);
379 if (RT_FAILURE(rc))
380 LogFunc(("Bulk mapping for read access failed. cPages=%zu fFlags=%#x rc=%Rrc\n", rc, cPages, fFlags));
381 }
382 else
383 LogFunc(("Bulk translation for read access failed. idDevice=%#x cPages=%zu rc=%Rrc\n", idDevice, cPages, rc));
384
385 RTMemFree(paGCPhysOut);
386 return rc;
387 }
388 return VERR_IOMMU_NOT_PRESENT;
389}
390
391
392/**
393 * Requests the mapping of multiple guest pages into ring-3 in prepartion for a bus
394 * master physical memory write operation.
395 *
396 * Refer pfnPhysBulkGCPhys2CCPtr() for further details.
397 *
398 * @returns VBox status code.
399 * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
400 *
401 * @param pDevIns The device instance.
402 * @param pPciDev The PCI device structure. Cannot be NULL.
403 * @param cPages Number of pages to lock.
404 * @param paGCPhysPages The guest physical address of the pages that
405 * should be mapped (@a cPages entries).
406 * @param fFlags Flags reserved for future use, MBZ.
407 * @param papvPages Where to store the ring-3 mapping addresses
408 * corresponding to @a paGCPhysPages.
409 * @param paLocks Where to store the locking information that
410 * pfnPhysBulkReleasePageMappingLock needs (@a cPages
411 * in length).
412 */
413int pdmR3IommuMemAccessBulkWriteCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t cPages, PCRTGCPHYS paGCPhysPages,
414 uint32_t fFlags, void **papvPages, PPGMPAGEMAPLOCK paLocks)
415{
416 PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
417 PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
418 if (pDevInsIommu)
419 {
420 if (pDevInsIommu != pDevIns)
421 { /* likely */ }
422 else
423 return VERR_IOMMU_CANNOT_CALL_SELF;
424
425 /* Allocate space for translated addresses. */
426 size_t const cbIovas = cPages * sizeof(uint64_t);
427 PRTGCPHYS paGCPhysOut = (PRTGCPHYS)RTMemAllocZ(cbIovas);
428 if (paGCPhysOut)
429 { /* likely */ }
430 else
431 {
432 LogFunc(("caller='%s'/%d: returns %Rrc - Failed to alloc %zu bytes for IOVA addresses\n",
433 pDevIns->pReg->szName, pDevIns->iInstance, VERR_NO_MEMORY, cbIovas));
434 return VERR_NO_MEMORY;
435 }
436
437 /* Ask the IOMMU for corresponding translated physical addresses. */
438 uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
439 AssertCompile(sizeof(RTGCPHYS) == sizeof(uint64_t));
440 int rc = pIommu->pfnMemBulkAccess(pDevInsIommu, idDevice, cPages, (uint64_t const *)paGCPhysPages, PDMIOMMU_MEM_F_WRITE,
441 paGCPhysOut);
442 if (RT_SUCCESS(rc))
443 {
444 /* Perform the bulk mapping but with the translated addresses. */
445 rc = pDevIns->pHlpR3->pfnPhysBulkGCPhys2CCPtr(pDevIns, cPages, paGCPhysOut, fFlags, papvPages, paLocks);
446 if (RT_FAILURE(rc))
447 LogFunc(("Bulk mapping of addresses failed. cPages=%zu fFlags=%#x rc=%Rrc\n", rc, cPages, fFlags));
448 }
449 else
450 LogFunc(("IOMMU bulk translation failed. idDevice=%#x cPages=%zu rc=%Rrc\n", idDevice, cPages, rc));
451
452 RTMemFree(paGCPhysOut);
453 return rc;
454 }
455 return VERR_IOMMU_NOT_PRESENT;
456}
457#endif /* IN_RING3 */
458
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