From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/VMM/VMMAll/PDMAllIommu.cpp | 468 ++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 src/VBox/VMM/VMMAll/PDMAllIommu.cpp (limited to 'src/VBox/VMM/VMMAll/PDMAllIommu.cpp') 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 . + * + * 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 +#include +#ifdef IN_RING3 +# include +#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 */ + -- cgit v1.2.3