diff options
Diffstat (limited to 'src/VBox/VMM/VMMR0/IOMR0Mmio.cpp')
-rw-r--r-- | src/VBox/VMM/VMMR0/IOMR0Mmio.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR0/IOMR0Mmio.cpp b/src/VBox/VMM/VMMR0/IOMR0Mmio.cpp new file mode 100644 index 00000000..45766568 --- /dev/null +++ b/src/VBox/VMM/VMMR0/IOMR0Mmio.cpp @@ -0,0 +1,378 @@ +/* $Id: IOMR0Mmio.cpp $ */ +/** @file + * IOM - Host Context Ring 0, MMIO. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_IOM_MMIO +#include <VBox/vmm/iom.h> +#include "IOMInternal.h" +#include <VBox/vmm/pdmdev.h> +#include <VBox/vmm/vmcc.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/process.h> +#include <iprt/string.h> + + + +/** + * Initializes the MMIO related members. + * + * @param pGVM Pointer to the global VM structure. + */ +void iomR0MmioInitPerVMData(PGVM pGVM) +{ + pGVM->iomr0.s.hMmioMapObj = NIL_RTR0MEMOBJ; + pGVM->iomr0.s.hMmioMemObj = NIL_RTR0MEMOBJ; +#ifdef VBOX_WITH_STATISTICS + pGVM->iomr0.s.hMmioStatsMapObj = NIL_RTR0MEMOBJ; + pGVM->iomr0.s.hMmioStatsMemObj = NIL_RTR0MEMOBJ; +#endif +} + + +/** + * Cleans up MMIO related resources. + */ +void iomR0MmioCleanupVM(PGVM pGVM) +{ + RTR0MemObjFree(pGVM->iomr0.s.hMmioMapObj, true /*fFreeMappings*/); + pGVM->iomr0.s.hMmioMapObj = NIL_RTR0MEMOBJ; + RTR0MemObjFree(pGVM->iomr0.s.hMmioMemObj, true /*fFreeMappings*/); + pGVM->iomr0.s.hMmioMemObj = NIL_RTR0MEMOBJ; +#ifdef VBOX_WITH_STATISTICS + RTR0MemObjFree(pGVM->iomr0.s.hMmioStatsMapObj, true /*fFreeMappings*/); + pGVM->iomr0.s.hMmioStatsMapObj = NIL_RTR0MEMOBJ; + RTR0MemObjFree(pGVM->iomr0.s.hMmioStatsMemObj, true /*fFreeMappings*/); + pGVM->iomr0.s.hMmioStatsMemObj = NIL_RTR0MEMOBJ; +#endif +} + + +/** + * Implements PDMDEVHLPR0::pfnMmioSetUpContext. + * + * @param pGVM The global (ring-0) VM structure. + * @param pDevIns The device instance. + * @param hRegion The MMIO region handle (already registered in + * ring-3). + * @param pfnWrite The write handler callback, optional. + * @param pfnRead The read handler callback, optional. + * @param pfnFill The fill handler callback, optional. + * @param pvUser User argument for the callbacks. + * @thread EMT(0) + * @note Only callable at VM creation time. + */ +VMMR0_INT_DECL(int) IOMR0MmioSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, PFNIOMMMIONEWWRITE pfnWrite, + PFNIOMMMIONEWREAD pfnRead, PFNIOMMMIONEWFILL pfnFill, void *pvUser) +{ + /* + * Validate input and state. + */ + VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT); + VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE); + AssertReturn(hRegion < pGVM->iomr0.s.cMmioAlloc, VERR_IOM_INVALID_MMIO_HANDLE); + AssertReturn(hRegion < pGVM->iom.s.cMmioRegs, VERR_IOM_INVALID_MMIO_HANDLE); + AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE); + AssertReturn(pDevIns->pDevInsForR3 != NIL_RTR3PTR && !(pDevIns->pDevInsForR3 & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER); + AssertReturn(pGVM->iomr0.s.paMmioRing3Regs[hRegion].pDevIns == pDevIns->pDevInsForR3, VERR_IOM_INVALID_MMIO_HANDLE); + AssertReturn(pGVM->iomr0.s.paMmioRegs[hRegion].pDevIns == NULL, VERR_WRONG_ORDER); + Assert(pGVM->iomr0.s.paMmioRegs[hRegion].idxSelf == hRegion); + + AssertReturn(pfnWrite || pfnRead || pfnFill, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfnWrite, VERR_INVALID_POINTER); + AssertPtrNullReturn(pfnRead, VERR_INVALID_POINTER); + AssertPtrNullReturn(pfnFill, VERR_INVALID_POINTER); + + uint32_t const fFlags = pGVM->iomr0.s.paMmioRing3Regs[hRegion].fFlags; + RTGCPHYS const cbRegion = pGVM->iomr0.s.paMmioRing3Regs[hRegion].cbRegion; + AssertMsgReturn(cbRegion > 0 && cbRegion <= _1T, ("cbRegion=%#RGp\n", cbRegion), VERR_IOM_INVALID_MMIO_HANDLE); + + /* + * Do the job. + */ + pGVM->iomr0.s.paMmioRegs[hRegion].cbRegion = cbRegion; + pGVM->iomr0.s.paMmioRegs[hRegion].pvUser = pvUser; + pGVM->iomr0.s.paMmioRegs[hRegion].pDevIns = pDevIns; + pGVM->iomr0.s.paMmioRegs[hRegion].pfnWriteCallback = pfnWrite; + pGVM->iomr0.s.paMmioRegs[hRegion].pfnReadCallback = pfnRead; + pGVM->iomr0.s.paMmioRegs[hRegion].pfnFillCallback = pfnFill; + pGVM->iomr0.s.paMmioRegs[hRegion].fFlags = fFlags; +#ifdef VBOX_WITH_STATISTICS + uint16_t const idxStats = pGVM->iomr0.s.paMmioRing3Regs[hRegion].idxStats; + pGVM->iomr0.s.paMmioRegs[hRegion].idxStats = (uint32_t)idxStats < pGVM->iomr0.s.cMmioStatsAllocation + ? idxStats : UINT16_MAX; +#else + pGVM->iomr0.s.paMmioRegs[hRegion].idxStats = UINT16_MAX; +#endif + + pGVM->iomr0.s.paMmioRing3Regs[hRegion].fRing0 = true; + + return VINF_SUCCESS; +} + + +/** + * Grows the MMIO registration (all contexts) and lookup tables. + * + * @returns VBox status code. + * @param pGVM The global (ring-0) VM structure. + * @param cReqMinEntries The minimum growth (absolute). + * @thread EMT(0) + * @note Only callable at VM creation time. + */ +VMMR0_INT_DECL(int) IOMR0MmioGrowRegistrationTables(PGVM pGVM, uint64_t cReqMinEntries) +{ + /* + * Validate input and state. + */ + VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT); + VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE); + AssertReturn(cReqMinEntries <= _4K, VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS); + uint32_t cNewEntries = (uint32_t)cReqMinEntries; + AssertReturn(cNewEntries >= pGVM->iom.s.cMmioAlloc, VERR_IOM_MMIO_IPE_1); + uint32_t const cOldEntries = pGVM->iomr0.s.cMmioAlloc; + ASMCompilerBarrier(); + AssertReturn(cNewEntries >= cOldEntries, VERR_IOM_MMIO_IPE_2); + AssertReturn(pGVM->iom.s.cMmioRegs >= pGVM->iomr0.s.cMmioMax, VERR_IOM_MMIO_IPE_3); + + /* + * Allocate the new tables. We use a single allocation for the three tables (ring-0, + * ring-3, lookup) and does a partial mapping of the result to ring-3. + */ + uint32_t const cbRing0 = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOENTRYR0), PAGE_SIZE); + uint32_t const cbRing3 = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOENTRYR3), PAGE_SIZE); + uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOLOOKUPENTRY), PAGE_SIZE); + uint32_t const cbNew = cbRing0 + cbRing3 + cbShared; + + /* Use the rounded up space as best we can. */ + cNewEntries = RT_MIN(RT_MIN(cbRing0 / sizeof(IOMMMIOENTRYR0), cbRing3 / sizeof(IOMMMIOENTRYR3)), + cbShared / sizeof(IOMMMIOLOOKUPENTRY)); + + RTR0MEMOBJ hMemObj; + int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/); + if (RT_SUCCESS(rc)) + { + /* + * Zero and map it. + */ + RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew); + + RTR0MEMOBJ hMapObj; + rc = RTR0MemObjMapUserEx(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, + RTR0ProcHandleSelf(), cbRing0, cbNew - cbRing0); + if (RT_SUCCESS(rc)) + { + PIOMMMIOENTRYR0 const paRing0 = (PIOMMMIOENTRYR0)RTR0MemObjAddress(hMemObj); + PIOMMMIOENTRYR3 const paRing3 = (PIOMMMIOENTRYR3)((uintptr_t)paRing0 + cbRing0); + PIOMMMIOLOOKUPENTRY const paLookup = (PIOMMMIOLOOKUPENTRY)((uintptr_t)paRing3 + cbRing3); + RTR3UINTPTR const uAddrRing3 = RTR0MemObjAddressR3(hMapObj); + + /* + * Copy over the old info and initialize the idxSelf and idxStats members. + */ + if (pGVM->iomr0.s.paMmioRegs != NULL) + { + memcpy(paRing0, pGVM->iomr0.s.paMmioRegs, sizeof(paRing0[0]) * cOldEntries); + memcpy(paRing3, pGVM->iomr0.s.paMmioRing3Regs, sizeof(paRing3[0]) * cOldEntries); + memcpy(paLookup, pGVM->iomr0.s.paMmioLookup, sizeof(paLookup[0]) * cOldEntries); + } + + size_t i = cbRing0 / sizeof(*paRing0); + while (i-- > cOldEntries) + { + paRing0[i].idxSelf = (uint16_t)i; + paRing0[i].idxStats = UINT16_MAX; + } + i = cbRing3 / sizeof(*paRing3); + while (i-- > cOldEntries) + { + paRing3[i].idxSelf = (uint16_t)i; + paRing3[i].idxStats = UINT16_MAX; + } + + /* + * Switch the memory handles. + */ + RTR0MEMOBJ hTmp = pGVM->iomr0.s.hMmioMapObj; + pGVM->iomr0.s.hMmioMapObj = hMapObj; + hMapObj = hTmp; + + hTmp = pGVM->iomr0.s.hMmioMemObj; + pGVM->iomr0.s.hMmioMemObj = hMemObj; + hMemObj = hTmp; + + /* + * Update the variables. + */ + pGVM->iomr0.s.paMmioRegs = paRing0; + pGVM->iomr0.s.paMmioRing3Regs = paRing3; + pGVM->iomr0.s.paMmioLookup = paLookup; + pGVM->iom.s.paMmioRegs = uAddrRing3; + pGVM->iom.s.paMmioLookup = uAddrRing3 + cbRing3; + pGVM->iom.s.cMmioAlloc = cNewEntries; + pGVM->iomr0.s.cMmioAlloc = cNewEntries; + + /* + * Free the old allocation. + */ + RTR0MemObjFree(hMapObj, true /*fFreeMappings*/); + } + RTR0MemObjFree(hMemObj, true /*fFreeMappings*/); + } + + return rc; +} + + +/** + * Grows the MMIO statistics table. + * + * @returns VBox status code. + * @param pGVM The global (ring-0) VM structure. + * @param cReqMinEntries The minimum growth (absolute). + * @thread EMT(0) + * @note Only callable at VM creation time. + */ +VMMR0_INT_DECL(int) IOMR0MmioGrowStatisticsTable(PGVM pGVM, uint64_t cReqMinEntries) +{ + /* + * Validate input and state. + */ + VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT); + VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE); + AssertReturn(cReqMinEntries <= _64K, VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS); + uint32_t cNewEntries = (uint32_t)cReqMinEntries; +#ifdef VBOX_WITH_STATISTICS + uint32_t const cOldEntries = pGVM->iomr0.s.cMmioStatsAllocation; + ASMCompilerBarrier(); +#else + uint32_t const cOldEntries = 0; +#endif + AssertReturn(cNewEntries > cOldEntries, VERR_IOM_MMIO_IPE_1); + AssertReturn(pGVM->iom.s.cMmioStatsAllocation == cOldEntries, VERR_IOM_MMIO_IPE_1); + AssertReturn(pGVM->iom.s.cMmioStats <= cOldEntries, VERR_IOM_MMIO_IPE_2); +#ifdef VBOX_WITH_STATISTICS + AssertReturn(!pGVM->iomr0.s.fMmioStatsFrozen, VERR_WRONG_ORDER); +#endif + + /* + * Allocate a new table, zero it and map it. + */ +#ifndef VBOX_WITH_STATISTICS + AssertFailedReturn(VERR_NOT_SUPPORTED); +#else + uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOSTATSENTRY), PAGE_SIZE); + cNewEntries = cbNew / sizeof(IOMMMIOSTATSENTRY); + + RTR0MEMOBJ hMemObj; + int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/); + if (RT_SUCCESS(rc)) + { + RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew); + + RTR0MEMOBJ hMapObj; + rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf()); + if (RT_SUCCESS(rc)) + { + PIOMMMIOSTATSENTRY pMmioStats = (PIOMMMIOSTATSENTRY)RTR0MemObjAddress(hMemObj); + + /* + * Anything to copy over and free up? + */ + if (pGVM->iomr0.s.paMmioStats) + memcpy(pMmioStats, pGVM->iomr0.s.paMmioStats, cOldEntries * sizeof(IOMMMIOSTATSENTRY)); + + /* + * Switch the memory handles. + */ + RTR0MEMOBJ hTmp = pGVM->iomr0.s.hMmioStatsMapObj; + pGVM->iomr0.s.hMmioStatsMapObj = hMapObj; + hMapObj = hTmp; + + hTmp = pGVM->iomr0.s.hMmioStatsMemObj; + pGVM->iomr0.s.hMmioStatsMemObj = hMemObj; + hMemObj = hTmp; + + /* + * Update the variables. + */ + pGVM->iomr0.s.paMmioStats = pMmioStats; + pGVM->iom.s.paMmioStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hMmioStatsMapObj); + pGVM->iom.s.cMmioStatsAllocation = cNewEntries; + pGVM->iomr0.s.cMmioStatsAllocation = cNewEntries; + + /* + * Free the old allocation. + */ + RTR0MemObjFree(hMapObj, true /*fFreeMappings*/); + } + RTR0MemObjFree(hMemObj, true /*fFreeMappings*/); + } + return rc; +#endif /* VBOX_WITH_STATISTICS */ +} + + +/** + * Called after all devices has been instantiated to copy over the statistics + * indices to the ring-0 MMIO registration table. + * + * This simplifies keeping statistics for MMIO ranges that are ring-3 only. + * + * @returns VBox status code. + * @param pGVM The global (ring-0) VM structure. + * @thread EMT(0) + * @note Only callable at VM creation time. + */ +VMMR0_INT_DECL(int) IOMR0MmioSyncStatisticsIndices(PGVM pGVM) +{ + VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT); + VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE); + +#ifdef VBOX_WITH_STATISTICS + /* + * First, freeze the statistics array: + */ + pGVM->iomr0.s.fMmioStatsFrozen = true; + + /* + * Second, synchronize the indices: + */ + uint32_t const cRegs = RT_MIN(pGVM->iom.s.cMmioRegs, pGVM->iomr0.s.cMmioAlloc); + uint32_t const cStatsAlloc = pGVM->iomr0.s.cMmioStatsAllocation; + PIOMMMIOENTRYR0 paMmioRegs = pGVM->iomr0.s.paMmioRegs; + IOMMMIOENTRYR3 const *paMmioRegsR3 = pGVM->iomr0.s.paMmioRing3Regs; + AssertReturn((paMmioRegs && paMmioRegsR3) || cRegs == 0, VERR_IOM_MMIO_IPE_3); + + for (uint32_t i = 0 ; i < cRegs; i++) + { + uint16_t idxStats = paMmioRegsR3[i].idxStats; + paMmioRegs[i].idxStats = idxStats < cStatsAlloc ? idxStats : UINT16_MAX; + } + +#else + RT_NOREF(pGVM); +#endif + return VINF_SUCCESS; +} + |