summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMAll/PGMAllMap.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/VMM/VMMAll/PGMAllMap.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMAll/PGMAllMap.cpp')
-rw-r--r--src/VBox/VMM/VMMAll/PGMAllMap.cpp937
1 files changed, 937 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/PGMAllMap.cpp b/src/VBox/VMM/VMMAll/PGMAllMap.cpp
new file mode 100644
index 00000000..79167e30
--- /dev/null
+++ b/src/VBox/VMM/VMMAll/PGMAllMap.cpp
@@ -0,0 +1,937 @@
+/* $Id: PGMAllMap.cpp $ */
+/** @file
+ * PGM - Page Manager and Monitor - All context code.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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_PGM
+#include <VBox/vmm/pgm.h>
+#include <VBox/vmm/em.h>
+#include "PGMInternal.h"
+#include <VBox/vmm/vm.h>
+#include "PGMInline.h"
+#include <VBox/err.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+
+
+/**
+ * Maps a range of physical pages at a given virtual address
+ * in the guest context.
+ *
+ * The GC virtual address range must be within an existing mapping.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr Where to map the page(s). Must be page aligned.
+ * @param HCPhys Start of the range of physical pages. Must be page aligned.
+ * @param cbPages Number of bytes to map. Must be page aligned.
+ * @param fFlags Page flags (X86_PTE_*).
+ */
+VMMDECL(int) PGMMap(PVM pVM, RTGCUINTPTR GCPtr, RTHCPHYS HCPhys, uint32_t cbPages, unsigned fFlags)
+{
+ AssertMsg(pVM->pgm.s.offVM, ("Bad init order\n"));
+
+ /*
+ * Validate input.
+ */
+ AssertMsg(RT_ALIGN_T(GCPtr, PAGE_SIZE, RTGCUINTPTR) == GCPtr, ("Invalid alignment GCPtr=%#x\n", GCPtr));
+ AssertMsg(cbPages > 0 && RT_ALIGN_32(cbPages, PAGE_SIZE) == cbPages, ("Invalid cbPages=%#x\n", cbPages));
+ AssertMsg(!(fFlags & X86_PDE_PG_MASK), ("Invalid flags %#x\n", fFlags));
+
+ /* hypervisor defaults */
+ if (!fFlags)
+ fFlags = X86_PTE_P | X86_PTE_A | X86_PTE_D;
+
+ /*
+ * Find the mapping.
+ */
+ PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings);
+ while (pCur)
+ {
+ if (GCPtr - pCur->GCPtr < pCur->cb)
+ {
+ if (GCPtr + cbPages - 1 > pCur->GCPtrLast)
+ {
+ AssertMsgFailed(("Invalid range!!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Setup PTE.
+ */
+ X86PTEPAE Pte;
+ Pte.u = fFlags | (HCPhys & X86_PTE_PAE_PG_MASK);
+
+ /*
+ * Update the page tables.
+ */
+ for (;;)
+ {
+ RTGCUINTPTR off = GCPtr - pCur->GCPtr;
+ const unsigned iPT = off >> X86_PD_SHIFT;
+ const unsigned iPageNo = (off >> PAGE_SHIFT) & X86_PT_MASK;
+
+ /* 32-bit */
+ pCur->aPTs[iPT].CTX_SUFF(pPT)->a[iPageNo].u = (uint32_t)Pte.u; /* ASSUMES HCPhys < 4GB and/or that we're never gonna do 32-bit on a PAE host! */
+
+ /* pae */
+ PGMSHWPTEPAE_SET(pCur->aPTs[iPT].CTX_SUFF(paPaePTs)[iPageNo / 512].a[iPageNo % 512], Pte.u);
+
+ /* next */
+ cbPages -= PAGE_SIZE;
+ if (!cbPages)
+ break;
+ GCPtr += PAGE_SIZE;
+ Pte.u += PAGE_SIZE;
+ }
+
+ return VINF_SUCCESS;
+ }
+
+ /* next */
+ pCur = pCur->CTX_SUFF(pNext);
+ }
+
+ AssertMsgFailed(("GCPtr=%#x was not found in any mapping ranges!\n", GCPtr));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Sets (replaces) the page flags for a range of pages in a mapping.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr Virtual address of the first page in the range.
+ * @param cb Size (in bytes) of the range to apply the modification to.
+ * @param fFlags Page flags X86_PTE_*, excluding the page mask of course.
+ */
+VMMDECL(int) PGMMapSetPage(PVM pVM, RTGCPTR GCPtr, uint64_t cb, uint64_t fFlags)
+{
+ return PGMMapModifyPage(pVM, GCPtr, cb, fFlags, 0);
+}
+
+
+/**
+ * Modify page flags for a range of pages in a mapping.
+ *
+ * The existing flags are ANDed with the fMask and ORed with the fFlags.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr Virtual address of the first page in the range.
+ * @param cb Size (in bytes) of the range to apply the modification to.
+ * @param fFlags The OR mask - page flags X86_PTE_*, excluding the page mask of course.
+ * @param fMask The AND mask - page flags X86_PTE_*, excluding the page mask of course.
+ */
+VMMDECL(int) PGMMapModifyPage(PVM pVM, RTGCPTR GCPtr, size_t cb, uint64_t fFlags, uint64_t fMask)
+{
+ /*
+ * Validate input.
+ */
+ AssertMsg(!(fFlags & (X86_PTE_PAE_PG_MASK | X86_PTE_PAE_MBZ_MASK_NX)), ("fFlags=%#x\n", fFlags));
+ Assert(cb);
+
+ /*
+ * Align the input.
+ */
+ cb += (RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK;
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ GCPtr = (RTGCPTR)((RTGCUINTPTR)GCPtr & PAGE_BASE_GC_MASK);
+
+ /*
+ * Find the mapping.
+ */
+ PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings);
+ while (pCur)
+ {
+ RTGCUINTPTR off = (RTGCUINTPTR)GCPtr - (RTGCUINTPTR)pCur->GCPtr;
+ if (off < pCur->cb)
+ {
+ AssertMsgReturn(off + cb <= pCur->cb,
+ ("Invalid page range %#x LB%#x. mapping '%s' %#x to %#x\n",
+ GCPtr, cb, pCur->pszDesc, pCur->GCPtr, pCur->GCPtrLast),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Perform the requested operation.
+ */
+ while (cb > 0)
+ {
+ unsigned iPT = off >> X86_PD_SHIFT;
+ unsigned iPTE = (off >> PAGE_SHIFT) & X86_PT_MASK;
+ while (cb > 0 && iPTE < RT_ELEMENTS(pCur->aPTs[iPT].CTX_SUFF(pPT)->a))
+ {
+ /* 32-Bit */
+ pCur->aPTs[iPT].CTX_SUFF(pPT)->a[iPTE].u &= fMask | X86_PTE_PG_MASK;
+ pCur->aPTs[iPT].CTX_SUFF(pPT)->a[iPTE].u |= fFlags & ~X86_PTE_PG_MASK;
+
+ /* PAE */
+ PPGMSHWPTEPAE pPtePae = &pCur->aPTs[iPT].CTX_SUFF(paPaePTs)[iPTE / 512].a[iPTE % 512];
+ PGMSHWPTEPAE_SET(*pPtePae,
+ ( PGMSHWPTEPAE_GET_U(*pPtePae)
+ & (fMask | X86_PTE_PAE_PG_MASK))
+ | (fFlags & ~(X86_PTE_PAE_PG_MASK | X86_PTE_PAE_MBZ_MASK_NX)));
+
+ /* invalidate tls */
+ PGM_INVL_PG(VMMGetCpu(pVM), (RTGCUINTPTR)pCur->GCPtr + off);
+
+ /* next */
+ iPTE++;
+ cb -= PAGE_SIZE;
+ off += PAGE_SIZE;
+ }
+ }
+
+ return VINF_SUCCESS;
+ }
+ /* next */
+ pCur = pCur->CTX_SUFF(pNext);
+ }
+
+ AssertMsgFailed(("Page range %#x LB%#x not found\n", GCPtr, cb));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Get information about a page in a mapping.
+ *
+ * This differs from PGMShwGetPage and PGMGstGetPage in that it only consults
+ * the page table to calculate the flags.
+ *
+ * @returns VINF_SUCCESS, VERR_PAGE_NOT_PRESENT or VERR_NOT_FOUND.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr The page address.
+ * @param pfFlags Where to return the flags. Optional.
+ * @param pHCPhys Where to return the address. Optional.
+ */
+VMMDECL(int) PGMMapGetPage(PVM pVM, RTGCPTR GCPtr, uint64_t *pfFlags, PRTHCPHYS pHCPhys)
+{
+ /*
+ * Find the mapping.
+ */
+ GCPtr &= PAGE_BASE_GC_MASK;
+ PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings);
+ while (pCur)
+ {
+ RTGCUINTPTR off = (RTGCUINTPTR)GCPtr - (RTGCUINTPTR)pCur->GCPtr;
+ if (off < pCur->cb)
+ {
+ /*
+ * Dig out the information.
+ */
+ int rc = VINF_SUCCESS;
+ unsigned iPT = off >> X86_PD_SHIFT;
+ unsigned iPTE = (off >> PAGE_SHIFT) & X86_PT_MASK;
+ PCPGMSHWPTEPAE pPtePae = &pCur->aPTs[iPT].CTX_SUFF(paPaePTs)[iPTE / 512].a[iPTE % 512];
+ if (PGMSHWPTEPAE_IS_P(*pPtePae))
+ {
+ if (pfFlags)
+ *pfFlags = PGMSHWPTEPAE_GET_U(*pPtePae) & ~X86_PTE_PAE_PG_MASK;
+ if (pHCPhys)
+ *pHCPhys = PGMSHWPTEPAE_GET_HCPHYS(*pPtePae);
+ }
+ else
+ rc = VERR_PAGE_NOT_PRESENT;
+ return rc;
+ }
+ /* next */
+ pCur = pCur->CTX_SUFF(pNext);
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+#ifndef PGM_WITHOUT_MAPPINGS
+
+/**
+ * Sets all PDEs involved with the mapping in the shadow page table.
+ *
+ * Ignored if mappings are disabled (i.e. if HM is enabled).
+ *
+ * @param pVM The cross context VM structure.
+ * @param pMap Pointer to the mapping in question.
+ * @param iNewPDE The index of the 32-bit PDE corresponding to the base of the mapping.
+ */
+void pgmMapSetShadowPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iNewPDE)
+{
+ Log4(("pgmMapSetShadowPDEs new pde %x (mappings enabled %d)\n", iNewPDE, pgmMapAreMappingsEnabled(pVM)));
+
+ if (!pgmMapAreMappingsEnabled(pVM))
+ return;
+
+ /* This only applies to raw mode where we only support 1 VCPU. */
+ PVMCPU pVCpu = VMMGetCpu0(pVM);
+ if (!pVCpu->pgm.s.CTX_SUFF(pShwPageCR3))
+ return; /* too early */
+
+ PGMMODE enmShadowMode = PGMGetShadowMode(pVCpu);
+ Assert(enmShadowMode <= PGMMODE_PAE_NX);
+
+ PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+
+ /*
+ * Insert the page tables into the shadow page directories.
+ */
+ unsigned i = pMap->cPTs;
+ iNewPDE += i;
+ while (i-- > 0)
+ {
+ iNewPDE--;
+
+ switch (enmShadowMode)
+ {
+ case PGMMODE_32_BIT:
+ {
+ PX86PD pShw32BitPd = pgmShwGet32BitPDPtr(pVCpu);
+ AssertFatal(pShw32BitPd);
+
+ /* Free any previous user, unless it's us. */
+ Assert( (pShw32BitPd->a[iNewPDE].u & (X86_PDE_P | PGM_PDFLAGS_MAPPING)) != (X86_PDE_P | PGM_PDFLAGS_MAPPING)
+ || (pShw32BitPd->a[iNewPDE].u & X86_PDE_PG_MASK) == pMap->aPTs[i].HCPhysPT);
+ if ( pShw32BitPd->a[iNewPDE].n.u1Present
+ && !(pShw32BitPd->a[iNewPDE].u & PGM_PDFLAGS_MAPPING))
+ pgmPoolFree(pVM, pShw32BitPd->a[iNewPDE].u & X86_PDE_PG_MASK, pVCpu->pgm.s.CTX_SUFF(pShwPageCR3)->idx, iNewPDE);
+
+ /* Default mapping page directory flags are read/write and supervisor; individual page attributes determine the final flags. */
+ pShw32BitPd->a[iNewPDE].u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US
+ | (uint32_t)pMap->aPTs[i].HCPhysPT;
+ PGM_DYNMAP_UNUSED_HINT_VM(pVM, pShw32BitPd);
+ break;
+ }
+
+ case PGMMODE_PAE:
+ case PGMMODE_PAE_NX:
+ {
+ const uint32_t iPdPt = iNewPDE / 256;
+ unsigned iPaePde = iNewPDE * 2 % 512;
+ PX86PDPT pShwPdpt = pgmShwGetPaePDPTPtr(pVCpu);
+ Assert(pShwPdpt);
+
+ /*
+ * Get the shadow PD.
+ * If no PD, sync it (PAE guest) or fake (not present or 32-bit guest).
+ * Note! The RW, US and A bits are reserved for PAE PDPTEs. Setting the
+ * accessed bit causes invalid VT-x guest state errors.
+ */
+ PX86PDPAE pShwPaePd = pgmShwGetPaePDPtr(pVCpu, iPdPt << X86_PDPT_SHIFT);
+ if (!pShwPaePd)
+ {
+ X86PDPE GstPdpe;
+ if (PGMGetGuestMode(pVCpu) < PGMMODE_PAE)
+ GstPdpe.u = X86_PDPE_P;
+ else
+ {
+ PX86PDPE pGstPdpe = pgmGstGetPaePDPEPtr(pVCpu, iPdPt << X86_PDPT_SHIFT);
+ if (pGstPdpe)
+ GstPdpe = *pGstPdpe;
+ else
+ GstPdpe.u = X86_PDPE_P;
+ }
+ int rc = pgmShwSyncPaePDPtr(pVCpu, iPdPt << X86_PDPT_SHIFT, GstPdpe.u, &pShwPaePd);
+ AssertFatalRC(rc);
+ }
+ Assert(pShwPaePd);
+
+ /*
+ * Mark the page as locked; disallow flushing.
+ */
+ PPGMPOOLPAGE pPoolPagePd = pgmPoolGetPage(pPool, pShwPdpt->a[iPdPt].u & X86_PDPE_PG_MASK);
+ AssertFatal(pPoolPagePd);
+ if (!pgmPoolIsPageLocked(pPoolPagePd))
+ pgmPoolLockPage(pPool, pPoolPagePd);
+# ifdef VBOX_STRICT
+ else if (pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING)
+ {
+ Assert(PGMGetGuestMode(pVCpu) >= PGMMODE_PAE); /** @todo We may hit this during reset, will fix later. */
+ AssertFatalMsg( (pShwPaePd->a[iPaePde].u & X86_PDE_PAE_PG_MASK) == pMap->aPTs[i].HCPhysPaePT0
+ || !PGMMODE_WITH_PAGING(PGMGetGuestMode(pVCpu)),
+ ("%RX64 vs %RX64\n", pShwPaePd->a[iPaePde+1].u & X86_PDE_PAE_PG_MASK, pMap->aPTs[i].HCPhysPaePT0));
+ Assert(pShwPaePd->a[iPaePde+1].u & PGM_PDFLAGS_MAPPING);
+ AssertFatalMsg( (pShwPaePd->a[iPaePde+1].u & X86_PDE_PAE_PG_MASK) == pMap->aPTs[i].HCPhysPaePT1
+ || !PGMMODE_WITH_PAGING(PGMGetGuestMode(pVCpu)),
+ ("%RX64 vs %RX64\n", pShwPaePd->a[iPaePde+1].u & X86_PDE_PAE_PG_MASK, pMap->aPTs[i].HCPhysPaePT1));
+ }
+# endif
+
+ /*
+ * Insert our first PT, freeing anything we might be replacing unless it's a mapping (i.e. us).
+ */
+ Assert( (pShwPaePd->a[iPaePde].u & (X86_PDE_P | PGM_PDFLAGS_MAPPING)) != (X86_PDE_P | PGM_PDFLAGS_MAPPING)
+ || (pShwPaePd->a[iPaePde].u & X86_PDE_PAE_PG_MASK) == pMap->aPTs[i].HCPhysPaePT0);
+ if ( pShwPaePd->a[iPaePde].n.u1Present
+ && !(pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING))
+ {
+ Assert(!(pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING));
+ pgmPoolFree(pVM, pShwPaePd->a[iPaePde].u & X86_PDE_PAE_PG_MASK, pPoolPagePd->idx, iPaePde);
+ }
+ pShwPaePd->a[iPaePde].u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US
+ | pMap->aPTs[i].HCPhysPaePT0;
+
+ /* 2nd 2 MB PDE of the 4 MB region, same as above. */
+ iPaePde++;
+ AssertFatal(iPaePde < 512);
+ Assert( (pShwPaePd->a[iPaePde].u & (X86_PDE_P | PGM_PDFLAGS_MAPPING)) != (X86_PDE_P | PGM_PDFLAGS_MAPPING)
+ || (pShwPaePd->a[iPaePde].u & X86_PDE_PAE_PG_MASK) == pMap->aPTs[i].HCPhysPaePT1);
+ if ( pShwPaePd->a[iPaePde].n.u1Present
+ && !(pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING))
+ pgmPoolFree(pVM, pShwPaePd->a[iPaePde].u & X86_PDE_PG_MASK, pPoolPagePd->idx, iPaePde);
+ pShwPaePd->a[iPaePde].u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US
+ | pMap->aPTs[i].HCPhysPaePT1;
+
+ /*
+ * Set the PGM_PDFLAGS_MAPPING flag in the page directory pointer entry. (legacy PAE guest mode)
+ */
+ pShwPdpt->a[iPdPt].u |= PGM_PLXFLAGS_MAPPING;
+
+ PGM_DYNMAP_UNUSED_HINT_VM(pVM, pShwPaePd);
+ PGM_DYNMAP_UNUSED_HINT_VM(pVM, pShwPdpt);
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+}
+
+
+/**
+ * Clears all PDEs involved with the mapping in the shadow page table.
+ *
+ * Ignored if mappings are disabled (i.e. if HM is enabled).
+ *
+ * @param pVM The cross context VM structure.
+ * @param pShwPageCR3 CR3 root page
+ * @param pMap Pointer to the mapping in question.
+ * @param iOldPDE The index of the 32-bit PDE corresponding to the base of the mapping.
+ * @param fDeactivateCR3 Set if it's pgmMapDeactivateCR3 calling.
+ */
+void pgmMapClearShadowPDEs(PVM pVM, PPGMPOOLPAGE pShwPageCR3, PPGMMAPPING pMap, unsigned iOldPDE, bool fDeactivateCR3)
+{
+ Log(("pgmMapClearShadowPDEs: old pde %x (cPTs=%x) (mappings enabled %d) fDeactivateCR3=%RTbool\n", iOldPDE, pMap->cPTs, pgmMapAreMappingsEnabled(pVM), fDeactivateCR3));
+
+ /*
+ * Skip this if it doesn't apply.
+ */
+ if (!pgmMapAreMappingsEnabled(pVM))
+ return;
+
+ Assert(pShwPageCR3);
+
+ /* This only applies to raw mode where we only support 1 VCPU. */
+ PVMCPU pVCpu = VMMGetCpu0(pVM);
+# ifdef IN_RC
+ Assert(pShwPageCR3 != pVCpu->pgm.s.CTX_SUFF(pShwPageCR3));
+# endif
+
+ PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+
+ PX86PDPT pCurrentShwPdpt = NULL;
+ if ( PGMGetGuestMode(pVCpu) >= PGMMODE_PAE
+ && pShwPageCR3 != pVCpu->pgm.s.CTX_SUFF(pShwPageCR3))
+ pCurrentShwPdpt = pgmShwGetPaePDPTPtr(pVCpu);
+
+ unsigned i = pMap->cPTs;
+ PGMMODE enmShadowMode = PGMGetShadowMode(pVCpu);
+
+ iOldPDE += i;
+ while (i-- > 0)
+ {
+ iOldPDE--;
+
+ switch(enmShadowMode)
+ {
+ case PGMMODE_32_BIT:
+ {
+ PX86PD pShw32BitPd = (PX86PD)PGMPOOL_PAGE_2_PTR_V2(pVM, pVCpu, pShwPageCR3);
+ AssertFatal(pShw32BitPd);
+
+ Assert(!pShw32BitPd->a[iOldPDE].n.u1Present || (pShw32BitPd->a[iOldPDE].u & PGM_PDFLAGS_MAPPING));
+ pShw32BitPd->a[iOldPDE].u = 0;
+ break;
+ }
+
+ case PGMMODE_PAE:
+ case PGMMODE_PAE_NX:
+ {
+ const unsigned iPdpt = iOldPDE / 256; /* iOldPDE * 2 / 512; iOldPDE is in 4 MB pages */
+ unsigned iPaePde = iOldPDE * 2 % 512;
+ PX86PDPT pShwPdpt = (PX86PDPT)PGMPOOL_PAGE_2_PTR_V2(pVM, pVCpu, pShwPageCR3);
+ PX86PDPAE pShwPaePd = pgmShwGetPaePDPtr(pVCpu, pShwPdpt, (iPdpt << X86_PDPT_SHIFT));
+
+ /*
+ * Clear the PGM_PDFLAGS_MAPPING flag for the page directory pointer entry. (legacy PAE guest mode)
+ */
+ if (fDeactivateCR3)
+ pShwPdpt->a[iPdpt].u &= ~PGM_PLXFLAGS_MAPPING;
+ else if (pShwPdpt->a[iPdpt].u & PGM_PLXFLAGS_MAPPING)
+ {
+ /* See if there are any other mappings here. This is suboptimal code. */
+ pShwPdpt->a[iPdpt].u &= ~PGM_PLXFLAGS_MAPPING;
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ if ( pCur != pMap
+ && ( (pCur->GCPtr >> X86_PDPT_SHIFT) == iPdpt
+ || (pCur->GCPtrLast >> X86_PDPT_SHIFT) == iPdpt))
+ {
+ pShwPdpt->a[iPdpt].u |= PGM_PLXFLAGS_MAPPING;
+ break;
+ }
+ }
+
+ /*
+ * If the page directory of the old CR3 is reused in the new one, then don't
+ * clear the hypervisor mappings.
+ */
+ if ( pCurrentShwPdpt
+ && (pCurrentShwPdpt->a[iPdpt].u & X86_PDPE_PG_MASK) == (pShwPdpt->a[iPdpt].u & X86_PDPE_PG_MASK) )
+ {
+ LogFlow(("pgmMapClearShadowPDEs: Pdpe %d reused -> don't clear hypervisor mappings!\n", iPdpt));
+ break;
+ }
+
+ /*
+ * Clear the mappings in the PD.
+ */
+ AssertFatal(pShwPaePd);
+ Assert(!pShwPaePd->a[iPaePde].n.u1Present || (pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING));
+ pShwPaePd->a[iPaePde].u = 0;
+
+ iPaePde++;
+ AssertFatal(iPaePde < 512);
+ Assert(!pShwPaePd->a[iPaePde].n.u1Present || (pShwPaePd->a[iPaePde].u & PGM_PDFLAGS_MAPPING));
+ pShwPaePd->a[iPaePde].u = 0;
+
+ /*
+ * Unlock the shadow pool PD page if the PDPTE no longer holds any mappings.
+ */
+ if ( fDeactivateCR3
+ || !(pShwPdpt->a[iPdpt].u & PGM_PLXFLAGS_MAPPING))
+ {
+ PPGMPOOLPAGE pPoolPagePd = pgmPoolGetPage(pPool, pShwPdpt->a[iPdpt].u & X86_PDPE_PG_MASK);
+ AssertFatal(pPoolPagePd);
+ if (pgmPoolIsPageLocked(pPoolPagePd))
+ pgmPoolUnlockPage(pPool, pPoolPagePd);
+ }
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+
+ PGM_DYNMAP_UNUSED_HINT_VM(pVM, pCurrentShwPdpt);
+}
+
+#endif /* PGM_WITHOUT_MAPPINGS */
+#if defined(VBOX_STRICT) && !defined(IN_RING0)
+
+/**
+ * Clears all PDEs involved with the mapping in the shadow page table.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param pShwPageCR3 CR3 root page
+ * @param pMap Pointer to the mapping in question.
+ * @param iPDE The index of the 32-bit PDE corresponding to the base of the mapping.
+ */
+static void pgmMapCheckShadowPDEs(PVM pVM, PVMCPU pVCpu, PPGMPOOLPAGE pShwPageCR3, PPGMMAPPING pMap, unsigned iPDE)
+{
+ Assert(pShwPageCR3);
+
+ uint32_t i = pMap->cPTs;
+ PGMMODE enmShadowMode = PGMGetShadowMode(pVCpu);
+ PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+
+ iPDE += i;
+ while (i-- > 0)
+ {
+ iPDE--;
+
+ switch (enmShadowMode)
+ {
+ case PGMMODE_32_BIT:
+ {
+ PCX86PD pShw32BitPd = (PCX86PD)PGMPOOL_PAGE_2_PTR_V2(pVM, pVCpu, pShwPageCR3);
+ AssertFatal(pShw32BitPd);
+
+ AssertMsg(pShw32BitPd->a[iPDE].u == (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | (uint32_t)pMap->aPTs[i].HCPhysPT),
+ ("Expected %x vs %x; iPDE=%#x %RGv %s\n",
+ pShw32BitPd->a[iPDE].u, (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | (uint32_t)pMap->aPTs[i].HCPhysPT),
+ iPDE, pMap->GCPtr, R3STRING(pMap->pszDesc) ));
+ break;
+ }
+
+ case PGMMODE_PAE:
+ case PGMMODE_PAE_NX:
+ {
+ const unsigned iPdpt = iPDE / 256; /* iPDE * 2 / 512; iPDE is in 4 MB pages */
+ unsigned iPaePDE = iPDE * 2 % 512;
+ PX86PDPT pShwPdpt = (PX86PDPT)PGMPOOL_PAGE_2_PTR_V2(pVM, pVCpu, pShwPageCR3);
+ PCX86PDPAE pShwPaePd = pgmShwGetPaePDPtr(pVCpu, pShwPdpt, iPdpt << X86_PDPT_SHIFT);
+ AssertFatal(pShwPaePd);
+
+ AssertMsg(pShwPaePd->a[iPaePDE].u == (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT0),
+ ("Expected %RX64 vs %RX64; iPDE=%#x iPdpt=%#x iPaePDE=%#x %RGv %s\n",
+ pShwPaePd->a[iPaePDE].u, (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT0),
+ iPDE, iPdpt, iPaePDE, pMap->GCPtr, R3STRING(pMap->pszDesc) ));
+
+ iPaePDE++;
+ AssertFatal(iPaePDE < 512);
+
+ AssertMsg(pShwPaePd->a[iPaePDE].u == (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT1),
+ ("Expected %RX64 vs %RX64; iPDE=%#x iPdpt=%#x iPaePDE=%#x %RGv %s\n",
+ pShwPaePd->a[iPaePDE].u, (PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT1),
+ iPDE, iPdpt, iPaePDE, pMap->GCPtr, R3STRING(pMap->pszDesc) ));
+
+ AssertMsg(pShwPdpt->a[iPdpt].u & PGM_PLXFLAGS_MAPPING,
+ ("%RX64; iPdpt=%#x iPDE=%#x iPaePDE=%#x %RGv %s\n",
+ pShwPdpt->a[iPdpt].u,
+ iPDE, iPdpt, iPaePDE, pMap->GCPtr, R3STRING(pMap->pszDesc) ));
+
+ PCPGMPOOLPAGE pPoolPagePd = pgmPoolGetPage(pPool, pShwPdpt->a[iPdpt].u & X86_PDPE_PG_MASK);
+ AssertFatal(pPoolPagePd);
+ AssertMsg(pPoolPagePd->cLocked, (".idx=%d .type=%d\n", pPoolPagePd->idx, pPoolPagePd->enmKind));
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+}
+
+
+/**
+ * Check the hypervisor mappings in the active CR3.
+ *
+ * Ignored if mappings are disabled (i.e. if HM is enabled).
+ *
+ * @param pVM The cross context VM structure.
+ */
+VMMDECL(void) PGMMapCheck(PVM pVM)
+{
+ /*
+ * Can skip this if mappings are disabled.
+ */
+ if (!pgmMapAreMappingsEnabled(pVM))
+ return;
+
+ /* This only applies to raw mode where we only support 1 VCPU. */
+ Assert(pVM->cCpus == 1);
+ PVMCPU pVCpu = VMMGetCpu0(pVM);
+ Assert(pVCpu->pgm.s.CTX_SUFF(pShwPageCR3));
+
+ /*
+ * Iterate mappings.
+ */
+ pgmLock(pVM); /* to avoid assertions */
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ {
+ unsigned iPDE = pCur->GCPtr >> X86_PD_SHIFT;
+ pgmMapCheckShadowPDEs(pVM, pVCpu, pVCpu->pgm.s.CTX_SUFF(pShwPageCR3), pCur, iPDE);
+ }
+ pgmUnlock(pVM);
+}
+
+#endif /* defined(VBOX_STRICT) && !defined(IN_RING0) */
+#ifndef PGM_WITHOUT_MAPPINGS
+
+/**
+ * Apply the hypervisor mappings to the active CR3.
+ *
+ * Ignored if mappings are disabled (i.e. if HM is enabled).
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pShwPageCR3 CR3 root page
+ */
+int pgmMapActivateCR3(PVM pVM, PPGMPOOLPAGE pShwPageCR3)
+{
+ RT_NOREF_PV(pShwPageCR3);
+
+ /*
+ * Skip this if it doesn't apply.
+ */
+ if (!pgmMapAreMappingsEnabled(pVM))
+ return VINF_SUCCESS;
+
+ /* Note! This might not be logged successfully in RC because we usually
+ cannot flush the log at this point. */
+ Log4(("pgmMapActivateCR3: fixed mappings=%RTbool idxShwPageCR3=%#x\n", pVM->pgm.s.fMappingsFixed, pShwPageCR3 ? pShwPageCR3->idx : NIL_PGMPOOL_IDX));
+
+#ifdef VBOX_STRICT
+ PVMCPU pVCpu = VMMGetCpu0(pVM);
+ Assert(pShwPageCR3 && pShwPageCR3 == pVCpu->pgm.s.CTX_SUFF(pShwPageCR3));
+#endif
+
+ /*
+ * Iterate mappings.
+ */
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ {
+ unsigned iPDE = pCur->GCPtr >> X86_PD_SHIFT;
+ pgmMapSetShadowPDEs(pVM, pCur, iPDE);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Remove the hypervisor mappings from the specified CR3
+ *
+ * Ignored if mappings are disabled (i.e. if HM is enabled).
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pShwPageCR3 CR3 root page
+ */
+int pgmMapDeactivateCR3(PVM pVM, PPGMPOOLPAGE pShwPageCR3)
+{
+ /*
+ * Skip this if it doesn't apply.
+ */
+ if (!pgmMapAreMappingsEnabled(pVM))
+ return VINF_SUCCESS;
+
+ Assert(pShwPageCR3);
+ Log4(("pgmMapDeactivateCR3: fixed mappings=%d idxShwPageCR3=%#x\n", pVM->pgm.s.fMappingsFixed, pShwPageCR3 ? pShwPageCR3->idx : NIL_PGMPOOL_IDX));
+
+ /*
+ * Iterate mappings.
+ */
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ {
+ unsigned iPDE = pCur->GCPtr >> X86_PD_SHIFT;
+ pgmMapClearShadowPDEs(pVM, pShwPageCR3, pCur, iPDE, true /*fDeactivateCR3*/);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks guest PD for conflicts with VMM GC mappings.
+ *
+ * @returns true if conflict detected.
+ * @returns false if not.
+ * @param pVM The cross context VM structure.
+ */
+VMMDECL(bool) PGMMapHasConflicts(PVM pVM)
+{
+ /*
+ * Can skip this if mappings are safely fixed.
+ */
+ if (!pgmMapAreMappingsFloating(pVM))
+ return false;
+ AssertReturn(pgmMapAreMappingsEnabled(pVM), false);
+
+ /* This only applies to raw mode where we only support 1 VCPU. */
+ PVMCPU pVCpu = &pVM->aCpus[0];
+
+ PGMMODE const enmGuestMode = PGMGetGuestMode(pVCpu);
+ Assert(enmGuestMode <= PGMMODE_PAE_NX);
+
+ /*
+ * Iterate mappings.
+ */
+ if (enmGuestMode == PGMMODE_32_BIT)
+ {
+ /*
+ * Resolve the page directory.
+ */
+ PX86PD pPD = pgmGstGet32bitPDPtr(pVCpu);
+ Assert(pPD);
+
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ {
+ unsigned iPDE = pCur->GCPtr >> X86_PD_SHIFT;
+ unsigned iPT = pCur->cPTs;
+ while (iPT-- > 0)
+ if ( pPD->a[iPDE + iPT].n.u1Present /** @todo PGMGstGetPDE. */
+ && (EMIsRawRing0Enabled(pVM) || pPD->a[iPDE + iPT].n.u1User))
+ {
+ STAM_COUNTER_INC(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3DetectedConflicts);
+
+# ifdef IN_RING3
+ Log(("PGMHasMappingConflicts: Conflict was detected at %08RX32 for mapping %s (32 bits)\n"
+ " iPDE=%#x iPT=%#x PDE=%RGp.\n",
+ (iPT + iPDE) << X86_PD_SHIFT, pCur->pszDesc,
+ iPDE, iPT, pPD->a[iPDE + iPT].au32[0]));
+# else
+ Log(("PGMHasMappingConflicts: Conflict was detected at %08RX32 for mapping (32 bits)\n"
+ " iPDE=%#x iPT=%#x PDE=%RGp.\n",
+ (iPT + iPDE) << X86_PD_SHIFT,
+ iPDE, iPT, pPD->a[iPDE + iPT].au32[0]));
+# endif
+ return true;
+ }
+ }
+ }
+ else if ( enmGuestMode == PGMMODE_PAE
+ || enmGuestMode == PGMMODE_PAE_NX)
+ {
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; pCur = pCur->CTX_SUFF(pNext))
+ {
+ RTGCPTR GCPtr = pCur->GCPtr;
+
+ unsigned iPT = pCur->cb >> X86_PD_PAE_SHIFT;
+ while (iPT-- > 0)
+ {
+ X86PDEPAE Pde = pgmGstGetPaePDE(pVCpu, GCPtr);
+
+ if ( Pde.n.u1Present
+ && (EMIsRawRing0Enabled(pVM) || Pde.n.u1User))
+ {
+ STAM_COUNTER_INC(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3DetectedConflicts);
+# ifdef IN_RING3
+ Log(("PGMHasMappingConflicts: Conflict was detected at %RGv for mapping %s (PAE)\n"
+ " PDE=%016RX64.\n",
+ GCPtr, pCur->pszDesc, Pde.u));
+# else
+ Log(("PGMHasMappingConflicts: Conflict was detected at %RGv for mapping (PAE)\n"
+ " PDE=%016RX64.\n",
+ GCPtr, Pde.u));
+# endif
+ return true;
+ }
+ GCPtr += (1 << X86_PD_PAE_SHIFT);
+ }
+ }
+ }
+ else
+ AssertFailed();
+
+ return false;
+}
+
+
+/**
+ * Checks and resolves (ring 3 only) guest conflicts with the guest mappings.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+int pgmMapResolveConflicts(PVM pVM)
+{
+ /* The caller is expected to check these two conditions. */
+ Assert(!pVM->pgm.s.fMappingsFixed);
+ Assert(pgmMapAreMappingsEnabled(pVM));
+
+ /* This only applies to raw mode where we only support 1 VCPU. */
+ Assert(pVM->cCpus == 1);
+ PVMCPU pVCpu = &pVM->aCpus[0];
+ PGMMODE const enmGuestMode = PGMGetGuestMode(pVCpu);
+ Assert(enmGuestMode <= PGMMODE_PAE_NX);
+
+ if (enmGuestMode == PGMMODE_32_BIT)
+ {
+ /*
+ * Resolve the page directory.
+ */
+ PX86PD pPD = pgmGstGet32bitPDPtr(pVCpu);
+ Assert(pPD);
+
+ /*
+ * Iterate mappings.
+ */
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur; )
+ {
+ PPGMMAPPING pNext = pCur->CTX_SUFF(pNext);
+ unsigned iPDE = pCur->GCPtr >> X86_PD_SHIFT;
+ unsigned iPT = pCur->cPTs;
+ while (iPT-- > 0)
+ {
+ if ( pPD->a[iPDE + iPT].n.u1Present /** @todo PGMGstGetPDE. */
+ && ( EMIsRawRing0Enabled(pVM)
+ || pPD->a[iPDE + iPT].n.u1User))
+ {
+ STAM_COUNTER_INC(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3DetectedConflicts);
+
+# ifdef IN_RING3
+ Log(("PGMHasMappingConflicts: Conflict was detected at %08RX32 for mapping %s (32 bits)\n"
+ " iPDE=%#x iPT=%#x PDE=%RGp.\n",
+ (iPT + iPDE) << X86_PD_SHIFT, pCur->pszDesc,
+ iPDE, iPT, pPD->a[iPDE + iPT].au32[0]));
+ int rc = pgmR3SyncPTResolveConflict(pVM, pCur, pPD, iPDE << X86_PD_SHIFT);
+ AssertRCReturn(rc, rc);
+ break;
+# else
+ Log(("PGMHasMappingConflicts: Conflict was detected at %08RX32 for mapping (32 bits)\n"
+ " iPDE=%#x iPT=%#x PDE=%RGp.\n",
+ (iPT + iPDE) << X86_PD_SHIFT,
+ iPDE, iPT, pPD->a[iPDE + iPT].au32[0]));
+ return VINF_PGM_SYNC_CR3;
+# endif
+ }
+ }
+ pCur = pNext;
+ }
+ }
+ else if ( enmGuestMode == PGMMODE_PAE
+ || enmGuestMode == PGMMODE_PAE_NX)
+ {
+ /*
+ * Iterate mappings.
+ */
+ for (PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings); pCur;)
+ {
+ PPGMMAPPING pNext = pCur->CTX_SUFF(pNext);
+ RTGCPTR GCPtr = pCur->GCPtr;
+ unsigned iPT = pCur->cb >> X86_PD_PAE_SHIFT;
+ while (iPT-- > 0)
+ {
+ X86PDEPAE Pde = pgmGstGetPaePDE(pVCpu, GCPtr);
+
+ if ( Pde.n.u1Present
+ && (EMIsRawRing0Enabled(pVM) || Pde.n.u1User))
+ {
+ STAM_COUNTER_INC(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3DetectedConflicts);
+#ifdef IN_RING3
+ Log(("PGMHasMappingConflicts: Conflict was detected at %RGv for mapping %s (PAE)\n"
+ " PDE=%016RX64.\n",
+ GCPtr, pCur->pszDesc, Pde.u));
+ int rc = pgmR3SyncPTResolveConflictPAE(pVM, pCur, pCur->GCPtr);
+ AssertRCReturn(rc, rc);
+ break;
+#else
+ Log(("PGMHasMappingConflicts: Conflict was detected at %RGv for mapping (PAE)\n"
+ " PDE=%016RX64.\n",
+ GCPtr, Pde.u));
+ return VINF_PGM_SYNC_CR3;
+#endif
+ }
+ GCPtr += (1 << X86_PD_PAE_SHIFT);
+ }
+ pCur = pNext;
+ }
+ }
+ else
+ AssertFailed();
+
+ Assert(!PGMMapHasConflicts(pVM));
+ return VINF_SUCCESS;
+}
+
+#endif /* PGM_WITHOUT_MAPPINGS */
+