summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMAll/PDMAllIommu.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/VMM/VMMAll/PDMAllIommu.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMAll/PDMAllIommu.cpp')
-rw-r--r--src/VBox/VMM/VMMAll/PDMAllIommu.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/PDMAllIommu.cpp b/src/VBox/VMM/VMMAll/PDMAllIommu.cpp
new file mode 100644
index 00000000..185faa65
--- /dev/null
+++ b/src/VBox/VMM/VMMAll/PDMAllIommu.cpp
@@ -0,0 +1,468 @@
+/* $Id: PDMAllIommu.cpp $ */
+/** @file
+ * PDM IOMMU - All Contexts.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_PDM
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include "PDMInternal.h"
+
+#include <VBox/vmm/vmcc.h>
+#include <iprt/string.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/**
+ * Gets the PDM IOMMU for the current context from the PDM device instance.
+ */
+#ifdef IN_RING0
+#define PDMDEVINS_TO_IOMMU(a_pDevIns) &(a_pDevIns)->Internal.s.pGVM->pdmr0.s.aIommus[0];
+#else
+#define PDMDEVINS_TO_IOMMU(a_pDevIns) &(a_pDevIns)->Internal.s.pVMR3->pdm.s.aIommus[0];
+#endif
+
+
+/**
+ * Gets the PCI device ID (Bus:Dev:Fn) for the given PCI device.
+ *
+ * @returns PCI device ID.
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ */
+DECL_FORCE_INLINE(uint16_t) pdmIommuGetPciDeviceId(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev)
+{
+ uint8_t const idxBus = pPciDev->Int.s.idxPdmBus;
+#if defined(IN_RING0)
+ PGVM pGVM = pDevIns->Internal.s.pGVM;
+ Assert(idxBus < RT_ELEMENTS(pGVM->pdmr0.s.aPciBuses));
+ PCPDMPCIBUSR0 pBus = &pGVM->pdmr0.s.aPciBuses[idxBus];
+#elif defined(IN_RING3)
+ PVM pVM = pDevIns->Internal.s.pVMR3;
+ Assert(idxBus < RT_ELEMENTS(pVM->pdm.s.aPciBuses));
+ PCPDMPCIBUS pBus = &pVM->pdm.s.aPciBuses[idxBus];
+#endif
+ return PCIBDF_MAKE(pBus->iBus, pPciDev->uDevFn);
+}
+
+
+/**
+ * Returns whether an IOMMU instance is present.
+ *
+ * @returns @c true if an IOMMU is present, @c false otherwise.
+ * @param pDevIns The device instance.
+ */
+bool pdmIommuIsPresent(PPDMDEVINS pDevIns)
+{
+#ifdef IN_RING0
+ PCPDMIOMMUR3 pIommuR3 = &pDevIns->Internal.s.pGVM->pdm.s.aIommus[0];
+#else
+ PCPDMIOMMUR3 pIommuR3 = &pDevIns->Internal.s.pVMR3->pdm.s.aIommus[0];
+#endif
+ return pIommuR3->pDevInsR3 != NULL;
+}
+
+
+/** @copydoc PDMIOMMUREGR3::pfnMsiRemap */
+int pdmIommuMsiRemap(PPDMDEVINS pDevIns, uint16_t idDevice, PCMSIMSG pMsiIn, PMSIMSG pMsiOut)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ Assert(pDevInsIommu);
+ if (pDevInsIommu != pDevIns)
+ return pIommu->pfnMsiRemap(pDevInsIommu, idDevice, pMsiIn, pMsiOut);
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+}
+
+
+/**
+ * Bus master physical memory read after translating the physical address using the
+ * IOMMU.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device. Cannot be NULL.
+ * @param GCPhys The guest-physical address to read.
+ * @param pvBuf Where to put the data read.
+ * @param cbRead How many bytes to read.
+ * @param fFlags Combination of PDM_DEVHLP_PHYS_RW_F_XXX.
+ *
+ * @thread Any.
+ */
+int pdmIommuMemAccessRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead, uint32_t fFlags)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ int rc = VINF_SUCCESS;
+ while (cbRead > 0)
+ {
+ RTGCPHYS GCPhysOut;
+ size_t cbContig;
+ rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys, cbRead, PDMIOMMU_MEM_F_READ, &GCPhysOut, &cbContig);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbContig > 0 && cbContig <= cbRead);
+ /** @todo Handle strict return codes from PGMPhysRead. */
+ rc = pDevIns->CTX_SUFF(pHlp)->pfnPhysRead(pDevIns, GCPhysOut, pvBuf, cbContig, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ cbRead -= cbContig;
+ pvBuf = (void *)((uintptr_t)pvBuf + cbContig);
+ GCPhys += cbContig;
+ }
+ else
+ break;
+ }
+ else
+ {
+ LogFunc(("IOMMU memory read failed. idDevice=%#x GCPhys=%#RGp cb=%zu rc=%Rrc\n", idDevice, GCPhys, cbRead, rc));
+
+ /*
+ * We should initialize the read buffer on failure for devices that don't check
+ * return codes (but would verify the data). But we still want to propagate the
+ * error code from the IOMMU to the device, see @bugref{9936#c3}.
+ */
+ memset(pvBuf, 0xff, cbRead);
+ break;
+ }
+ }
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+
+
+/**
+ * Bus master physical memory write after translating the physical address using the
+ * IOMMU.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ * @param GCPhys The guest-physical address to write.
+ * @param pvBuf The data to write.
+ * @param cbWrite How many bytes to write.
+ * @param fFlags Combination of PDM_DEVHLP_PHYS_RW_F_XXX.
+ *
+ * @thread Any.
+ */
+int pdmIommuMemAccessWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite,
+ uint32_t fFlags)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ int rc = VINF_SUCCESS;
+ while (cbWrite > 0)
+ {
+ RTGCPHYS GCPhysOut;
+ size_t cbContig;
+ rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys, cbWrite, PDMIOMMU_MEM_F_WRITE, &GCPhysOut, &cbContig);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbContig > 0 && cbContig <= cbWrite);
+ /** @todo Handle strict return codes from PGMPhysWrite. */
+ rc = pDevIns->CTX_SUFF(pHlp)->pfnPhysWrite(pDevIns, GCPhysOut, pvBuf, cbContig, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ cbWrite -= cbContig;
+ pvBuf = (const void *)((uintptr_t)pvBuf + cbContig);
+ GCPhys += cbContig;
+ }
+ else
+ break;
+ }
+ else
+ {
+ LogFunc(("IOMMU memory write failed. idDevice=%#x GCPhys=%#RGp cb=%zu rc=%Rrc\n", idDevice, GCPhys, cbWrite, rc));
+ break;
+ }
+ }
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+
+
+#ifdef IN_RING3
+/**
+ * Requests the mapping of a guest page into ring-3 in preparation for a bus master
+ * physical memory read operation.
+ *
+ * Refer pfnPhysGCPhys2CCPtrReadOnly() for further details.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ * @param GCPhys The guest physical address of the page that should be
+ * mapped.
+ * @param fFlags Flags reserved for future use, MBZ.
+ * @param ppv Where to store the address corresponding to GCPhys.
+ * @param pLock Where to store the lock information that
+ * pfnPhysReleasePageMappingLock needs.
+ */
+int pdmR3IommuMemAccessReadCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, uint32_t fFlags, void const **ppv,
+ PPGMPAGEMAPLOCK pLock)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ size_t cbContig = 0;
+ RTGCPHYS GCPhysOut = NIL_RTGCPHYS;
+ int rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys & X86_PAGE_BASE_MASK, X86_PAGE_SIZE, PDMIOMMU_MEM_F_READ,
+ &GCPhysOut, &cbContig);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(GCPhysOut != NIL_RTGCPHYS);
+ Assert(cbContig == X86_PAGE_SIZE);
+ return pDevIns->pHlpR3->pfnPhysGCPhys2CCPtrReadOnly(pDevIns, GCPhysOut, fFlags, ppv, pLock);
+ }
+
+ LogFunc(("IOMMU memory read for pointer access failed. idDevice=%#x GCPhys=%#RGp rc=%Rrc\n", idDevice, GCPhys, rc));
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+
+
+/**
+ * Requests the mapping of a guest page into ring-3 in preparation for a bus master
+ * physical memory write operation.
+ *
+ * Refer pfnPhysGCPhys2CCPtr() for further details.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ * @param GCPhys The guest physical address of the page that should be
+ * mapped.
+ * @param fFlags Flags reserved for future use, MBZ.
+ * @param ppv Where to store the address corresponding to GCPhys.
+ * @param pLock Where to store the lock information that
+ * pfnPhysReleasePageMappingLock needs.
+ */
+int pdmR3IommuMemAccessWriteCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, RTGCPHYS GCPhys, uint32_t fFlags, void **ppv,
+ PPGMPAGEMAPLOCK pLock)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ size_t cbContig = 0;
+ RTGCPHYS GCPhysOut = NIL_RTGCPHYS;
+ int rc = pIommu->pfnMemAccess(pDevInsIommu, idDevice, GCPhys & X86_PAGE_BASE_MASK, X86_PAGE_SIZE, PDMIOMMU_MEM_F_WRITE,
+ &GCPhysOut, &cbContig);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(GCPhysOut != NIL_RTGCPHYS);
+ Assert(cbContig == X86_PAGE_SIZE);
+ return pDevIns->pHlpR3->pfnPhysGCPhys2CCPtr(pDevIns, GCPhysOut, fFlags, ppv, pLock);
+ }
+
+ LogFunc(("IOMMU memory write for pointer access failed. idDevice=%#x GCPhys=%#RGp rc=%Rrc\n", idDevice, GCPhys, rc));
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+
+
+/**
+ * Requests the mapping of multiple guest pages into ring-3 in prepartion for a bus
+ * master physical memory read operation.
+ *
+ * Refer pfnPhysBulkGCPhys2CCPtrReadOnly() for further details.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ * @param cPages Number of pages to lock.
+ * @param paGCPhysPages The guest physical address of the pages that
+ * should be mapped (@a cPages entries).
+ * @param fFlags Flags reserved for future use, MBZ.
+ * @param papvPages Where to store the ring-3 mapping addresses
+ * corresponding to @a paGCPhysPages.
+ * @param paLocks Where to store the locking information that
+ * pfnPhysBulkReleasePageMappingLock needs (@a cPages
+ * in length).
+ */
+int pdmR3IommuMemAccessBulkReadCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t cPages, PCRTGCPHYS paGCPhysPages,
+ uint32_t fFlags, const void **papvPages, PPGMPAGEMAPLOCK paLocks)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ /* Allocate space for translated addresses. */
+ size_t const cbIovas = cPages * sizeof(uint64_t);
+ PRTGCPHYS paGCPhysOut = (PRTGCPHYS)RTMemAllocZ(cbIovas);
+ if (paGCPhysOut)
+ { /* likely */ }
+ else
+ {
+ LogFunc(("caller='%s'/%d: returns %Rrc - Failed to alloc %zu bytes for IOVA addresses\n",
+ pDevIns->pReg->szName, pDevIns->iInstance, VERR_NO_MEMORY, cbIovas));
+ return VERR_NO_MEMORY;
+ }
+
+ /* Ask the IOMMU for corresponding translated physical addresses. */
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ AssertCompile(sizeof(RTGCPHYS) == sizeof(uint64_t));
+ int rc = pIommu->pfnMemBulkAccess(pDevInsIommu, idDevice, cPages, (uint64_t const *)paGCPhysPages, PDMIOMMU_MEM_F_READ,
+ paGCPhysOut);
+ if (RT_SUCCESS(rc))
+ {
+ /* Perform the bulk mapping but with the translated addresses. */
+ rc = pDevIns->pHlpR3->pfnPhysBulkGCPhys2CCPtrReadOnly(pDevIns, cPages, paGCPhysOut, fFlags, papvPages, paLocks);
+ if (RT_FAILURE(rc))
+ LogFunc(("Bulk mapping for read access failed. cPages=%zu fFlags=%#x rc=%Rrc\n", rc, cPages, fFlags));
+ }
+ else
+ LogFunc(("Bulk translation for read access failed. idDevice=%#x cPages=%zu rc=%Rrc\n", idDevice, cPages, rc));
+
+ RTMemFree(paGCPhysOut);
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+
+
+/**
+ * Requests the mapping of multiple guest pages into ring-3 in prepartion for a bus
+ * master physical memory write operation.
+ *
+ * Refer pfnPhysBulkGCPhys2CCPtr() for further details.
+ *
+ * @returns VBox status code.
+ * @retval VERR_IOMMU_NOT_PRESENT if an IOMMU is not present.
+ *
+ * @param pDevIns The device instance.
+ * @param pPciDev The PCI device structure. Cannot be NULL.
+ * @param cPages Number of pages to lock.
+ * @param paGCPhysPages The guest physical address of the pages that
+ * should be mapped (@a cPages entries).
+ * @param fFlags Flags reserved for future use, MBZ.
+ * @param papvPages Where to store the ring-3 mapping addresses
+ * corresponding to @a paGCPhysPages.
+ * @param paLocks Where to store the locking information that
+ * pfnPhysBulkReleasePageMappingLock needs (@a cPages
+ * in length).
+ */
+int pdmR3IommuMemAccessBulkWriteCCPtr(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t cPages, PCRTGCPHYS paGCPhysPages,
+ uint32_t fFlags, void **papvPages, PPGMPAGEMAPLOCK paLocks)
+{
+ PPDMIOMMU pIommu = PDMDEVINS_TO_IOMMU(pDevIns);
+ PPDMDEVINS pDevInsIommu = pIommu->CTX_SUFF(pDevIns);
+ if (pDevInsIommu)
+ {
+ if (pDevInsIommu != pDevIns)
+ { /* likely */ }
+ else
+ return VERR_IOMMU_CANNOT_CALL_SELF;
+
+ /* Allocate space for translated addresses. */
+ size_t const cbIovas = cPages * sizeof(uint64_t);
+ PRTGCPHYS paGCPhysOut = (PRTGCPHYS)RTMemAllocZ(cbIovas);
+ if (paGCPhysOut)
+ { /* likely */ }
+ else
+ {
+ LogFunc(("caller='%s'/%d: returns %Rrc - Failed to alloc %zu bytes for IOVA addresses\n",
+ pDevIns->pReg->szName, pDevIns->iInstance, VERR_NO_MEMORY, cbIovas));
+ return VERR_NO_MEMORY;
+ }
+
+ /* Ask the IOMMU for corresponding translated physical addresses. */
+ uint16_t const idDevice = pdmIommuGetPciDeviceId(pDevIns, pPciDev);
+ AssertCompile(sizeof(RTGCPHYS) == sizeof(uint64_t));
+ int rc = pIommu->pfnMemBulkAccess(pDevInsIommu, idDevice, cPages, (uint64_t const *)paGCPhysPages, PDMIOMMU_MEM_F_WRITE,
+ paGCPhysOut);
+ if (RT_SUCCESS(rc))
+ {
+ /* Perform the bulk mapping but with the translated addresses. */
+ rc = pDevIns->pHlpR3->pfnPhysBulkGCPhys2CCPtr(pDevIns, cPages, paGCPhysOut, fFlags, papvPages, paLocks);
+ if (RT_FAILURE(rc))
+ LogFunc(("Bulk mapping of addresses failed. cPages=%zu fFlags=%#x rc=%Rrc\n", rc, cPages, fFlags));
+ }
+ else
+ LogFunc(("IOMMU bulk translation failed. idDevice=%#x cPages=%zu rc=%Rrc\n", idDevice, cPages, rc));
+
+ RTMemFree(paGCPhysOut);
+ return rc;
+ }
+ return VERR_IOMMU_NOT_PRESENT;
+}
+#endif /* IN_RING3 */
+