summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/PGMMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/PGMMap.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PGMMap.cpp1470
1 files changed, 1470 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PGMMap.cpp b/src/VBox/VMM/VMMR3/PGMMap.cpp
new file mode 100644
index 00000000..949070a5
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/PGMMap.cpp
@@ -0,0 +1,1470 @@
+/* $Id: PGMMap.cpp $ */
+/** @file
+ * PGM - Page Manager, Guest Context Mappings.
+ */
+
+/*
+ * 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/dbgf.h>
+#include <VBox/vmm/pgm.h>
+#include "PGMInternal.h"
+#include <VBox/vmm/vm.h>
+#include "PGMInline.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifndef PGM_WITHOUT_MAPPINGS
+static void pgmR3MapClearPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iOldPDE);
+static void pgmR3MapSetPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iNewPDE);
+static int pgmR3MapIntermediateCheckOne(PVM pVM, uintptr_t uAddress, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault);
+static void pgmR3MapIntermediateDoOne(PVM pVM, uintptr_t uAddress, RTHCPHYS HCPhys, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault);
+#else
+# define pgmR3MapClearPDEs(pVM, pMap, iNewPDE) do { } while (0)
+# define pgmR3MapSetPDEs(pVM, pMap, iNewPDE) do { } while (0)
+#endif
+
+
+/**
+ * Creates a page table based mapping in GC.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr Virtual Address. (Page table aligned!)
+ * @param cb Size of the range. Must be a 4MB aligned!
+ * @param fFlags PGMR3MAPPT_FLAGS_UNMAPPABLE or 0.
+ * @param pfnRelocate Relocation callback function.
+ * @param pvUser User argument to the callback.
+ * @param pszDesc Pointer to description string. This must not be freed.
+ */
+VMMR3DECL(int) PGMR3MapPT(PVM pVM, RTGCPTR GCPtr, uint32_t cb, uint32_t fFlags, PFNPGMRELOCATE pfnRelocate, void *pvUser, const char *pszDesc)
+{
+ LogFlow(("PGMR3MapPT: GCPtr=%#x cb=%d fFlags=%#x pfnRelocate=%p pvUser=%p pszDesc=%s\n", GCPtr, cb, fFlags, pfnRelocate, pvUser, pszDesc));
+ AssertMsg(pVM->pgm.s.pInterPD, ("Paging isn't initialized, init order problems!\n"));
+
+ /*
+ * Validate input.
+ * Note! The lower limit (1 MB) matches how pgmR3PhysMMIOExCreate works.
+ */
+ Assert(!fFlags || fFlags == PGMR3MAPPT_FLAGS_UNMAPPABLE);
+ AssertMsgReturn(cb >= _1M && cb <= _64M, ("Seriously? cb=%d (%#x)\n", cb, cb), VERR_OUT_OF_RANGE);
+
+ cb = RT_ALIGN_32(cb, _4M);
+ RTGCPTR GCPtrLast = GCPtr + cb - 1;
+
+ AssertMsgReturn(GCPtrLast >= GCPtr, ("Range wraps! GCPtr=%x GCPtrLast=%x\n", GCPtr, GCPtrLast),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!pVM->pgm.s.fMappingsFixed, ("Mappings are fixed! It's not possible to add new mappings at this time!\n"),
+ VERR_PGM_MAPPINGS_FIXED);
+ AssertPtrReturn(pfnRelocate, VERR_INVALID_PARAMETER);
+
+ /*
+ * Find list location.
+ */
+ PPGMMAPPING pPrev = NULL;
+ PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ if (pCur->GCPtrLast >= GCPtr && pCur->GCPtr <= GCPtrLast)
+ {
+ AssertMsgFailed(("Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
+ pCur->pszDesc, GCPtr, GCPtrLast, pCur->GCPtr, pCur->GCPtrLast));
+ LogRel(("VERR_PGM_MAPPING_CONFLICT: Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
+ pCur->pszDesc, GCPtr, GCPtrLast, pCur->GCPtr, pCur->GCPtrLast));
+ return VERR_PGM_MAPPING_CONFLICT;
+ }
+ if (pCur->GCPtr > GCPtr)
+ break;
+ pPrev = pCur;
+ pCur = pCur->pNextR3;
+ }
+
+ /*
+ * Check for conflicts with intermediate mappings.
+ */
+ const unsigned iPageDir = GCPtr >> X86_PD_SHIFT;
+ const unsigned cPTs = cb >> X86_PD_SHIFT;
+ if (pVM->pgm.s.fFinalizedMappings)
+ {
+ for (unsigned i = 0; i < cPTs; i++)
+ if (pVM->pgm.s.pInterPD->a[iPageDir + i].n.u1Present)
+ {
+ AssertMsgFailed(("Address %#x is already in use by an intermediate mapping.\n", GCPtr + (i << PAGE_SHIFT)));
+ LogRel(("VERR_PGM_MAPPING_CONFLICT: Address %#x is already in use by an intermediate mapping.\n", GCPtr + (i << PAGE_SHIFT)));
+ return VERR_PGM_MAPPING_CONFLICT;
+ }
+ /** @todo AMD64: add check in PAE structures too, so we can remove all the 32-Bit paging stuff there. */
+ }
+
+ /*
+ * Allocate and initialize the new list node.
+ */
+ PPGMMAPPING pNew;
+ int rc;
+ if (fFlags & PGMR3MAPPT_FLAGS_UNMAPPABLE)
+ rc = MMHyperAlloc( pVM, RT_UOFFSETOF_DYN(PGMMAPPING, aPTs[cPTs]), 0, MM_TAG_PGM_MAPPINGS, (void **)&pNew);
+ else
+ rc = MMR3HyperAllocOnceNoRel(pVM, RT_UOFFSETOF_DYN(PGMMAPPING, aPTs[cPTs]), 0, MM_TAG_PGM_MAPPINGS, (void **)&pNew);
+ if (RT_FAILURE(rc))
+ return rc;
+ pNew->GCPtr = GCPtr;
+ pNew->GCPtrLast = GCPtrLast;
+ pNew->cb = cb;
+ pNew->pfnRelocate = pfnRelocate;
+ pNew->pvUser = pvUser;
+ pNew->pszDesc = pszDesc;
+ pNew->cPTs = cPTs;
+
+ /*
+ * Allocate page tables and insert them into the page directories.
+ * (One 32-bit PT and two PAE PTs.)
+ */
+ uint8_t *pbPTs;
+ if (fFlags & PGMR3MAPPT_FLAGS_UNMAPPABLE)
+ rc = MMHyperAlloc( pVM, PAGE_SIZE * 3 * cPTs, PAGE_SIZE, MM_TAG_PGM_MAPPINGS, (void **)&pbPTs);
+ else
+ rc = MMR3HyperAllocOnceNoRel(pVM, PAGE_SIZE * 3 * cPTs, PAGE_SIZE, MM_TAG_PGM_MAPPINGS, (void **)&pbPTs);
+ if (RT_FAILURE(rc))
+ {
+ MMHyperFree(pVM, pNew);
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Init the page tables and insert them into the page directories.
+ */
+ Log4(("PGMR3MapPT: GCPtr=%RGv cPTs=%u pbPTs=%p\n", GCPtr, cPTs, pbPTs));
+ for (unsigned i = 0; i < cPTs; i++)
+ {
+ /*
+ * 32-bit.
+ */
+ pNew->aPTs[i].pPTR3 = (PX86PT)pbPTs;
+ pNew->aPTs[i].pPTRC = MMHyperR3ToRC(pVM, pNew->aPTs[i].pPTR3);
+ pNew->aPTs[i].pPTR0 = MMHyperR3ToR0(pVM, pNew->aPTs[i].pPTR3);
+ pNew->aPTs[i].HCPhysPT = MMR3HyperHCVirt2HCPhys(pVM, pNew->aPTs[i].pPTR3);
+ pbPTs += PAGE_SIZE;
+ Log4(("PGMR3MapPT: i=%d: pPTR3=%RHv pPTRC=%RRv pPRTR0=%RHv HCPhysPT=%RHp\n",
+ i, pNew->aPTs[i].pPTR3, pNew->aPTs[i].pPTRC, pNew->aPTs[i].pPTR0, pNew->aPTs[i].HCPhysPT));
+
+ /*
+ * PAE.
+ */
+ pNew->aPTs[i].HCPhysPaePT0 = MMR3HyperHCVirt2HCPhys(pVM, pbPTs);
+ pNew->aPTs[i].HCPhysPaePT1 = MMR3HyperHCVirt2HCPhys(pVM, pbPTs + PAGE_SIZE);
+ pNew->aPTs[i].paPaePTsR3 = (PPGMSHWPTPAE)pbPTs;
+ pNew->aPTs[i].paPaePTsRC = MMHyperR3ToRC(pVM, pbPTs);
+ pNew->aPTs[i].paPaePTsR0 = MMHyperR3ToR0(pVM, pbPTs);
+ pbPTs += PAGE_SIZE * 2;
+ Log4(("PGMR3MapPT: i=%d: paPaePTsR#=%RHv paPaePTsRC=%RRv paPaePTsR#=%RHv HCPhysPaePT0=%RHp HCPhysPaePT1=%RHp\n",
+ i, pNew->aPTs[i].paPaePTsR3, pNew->aPTs[i].paPaePTsRC, pNew->aPTs[i].paPaePTsR0, pNew->aPTs[i].HCPhysPaePT0, pNew->aPTs[i].HCPhysPaePT1));
+ }
+ if (pVM->pgm.s.fFinalizedMappings)
+ pgmR3MapSetPDEs(pVM, pNew, iPageDir);
+ /* else PGMR3FinalizeMappings() */
+
+ /*
+ * Insert the new mapping.
+ */
+ pNew->pNextR3 = pCur;
+ pNew->pNextRC = pCur ? MMHyperR3ToRC(pVM, pCur) : NIL_RTRCPTR;
+ pNew->pNextR0 = pCur ? MMHyperR3ToR0(pVM, pCur) : NIL_RTR0PTR;
+ if (pPrev)
+ {
+ pPrev->pNextR3 = pNew;
+ pPrev->pNextRC = MMHyperR3ToRC(pVM, pNew);
+ pPrev->pNextR0 = MMHyperR3ToR0(pVM, pNew);
+ }
+ else
+ {
+ pVM->pgm.s.pMappingsR3 = pNew;
+ pVM->pgm.s.pMappingsRC = MMHyperR3ToRC(pVM, pNew);
+ pVM->pgm.s.pMappingsR0 = MMHyperR3ToR0(pVM, pNew);
+ }
+
+ for (VMCPUID i = 0; i < pVM->cCpus; i++)
+ {
+ PVMCPU pVCpu = &pVM->aCpus[i];
+ VMCPU_FF_SET(pVCpu, VMCPU_FF_PGM_SYNC_CR3);
+ }
+ return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_UNUSED_CODE
+
+/**
+ * Removes a page table based mapping.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtr Virtual Address. (Page table aligned!)
+ *
+ * @remarks Don't call this without passing PGMR3MAPPT_FLAGS_UNMAPPABLE to
+ * PGMR3MapPT or you'll burn in the heap.
+ */
+VMMR3DECL(int) PGMR3UnmapPT(PVM pVM, RTGCPTR GCPtr)
+{
+ LogFlow(("PGMR3UnmapPT: GCPtr=%#x\n", GCPtr));
+ AssertReturn(pVM->pgm.s.fFinalizedMappings, VERR_WRONG_ORDER);
+
+ /*
+ * Find it.
+ */
+ PPGMMAPPING pPrev = NULL;
+ PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ if (pCur->GCPtr == GCPtr)
+ {
+ /*
+ * Unlink it.
+ */
+ if (pPrev)
+ {
+ pPrev->pNextR3 = pCur->pNextR3;
+ pPrev->pNextRC = pCur->pNextRC;
+ pPrev->pNextR0 = pCur->pNextR0;
+ }
+ else
+ {
+ pVM->pgm.s.pMappingsR3 = pCur->pNextR3;
+ pVM->pgm.s.pMappingsRC = pCur->pNextRC;
+ pVM->pgm.s.pMappingsR0 = pCur->pNextR0;
+ }
+
+ /*
+ * Free the page table memory, clear page directory entries
+ * and free the page tables and node memory.
+ */
+ MMHyperFree(pVM, pCur->aPTs[0].pPTR3);
+ if (pCur->GCPtr != NIL_RTGCPTR)
+ pgmR3MapClearPDEs(pVM, pCur, pCur->GCPtr >> X86_PD_SHIFT);
+ MMHyperFree(pVM, pCur);
+
+ for (VMCPUID i = 0; i < pVM->cCpus; i++)
+ {
+ PVMCPU pVCpu = &pVM->aCpus[i];
+ VMCPU_FF_SET(pVCpu, VMCPU_FF_PGM_SYNC_CR3);
+ }
+ return VINF_SUCCESS;
+ }
+
+ /* done? */
+ if (pCur->GCPtr > GCPtr)
+ break;
+
+ /* next */
+ pPrev = pCur;
+ pCur = pCur->pNextR3;
+ }
+
+ AssertMsgFailed(("No mapping for %#x found!\n", GCPtr));
+ return VERR_INVALID_PARAMETER;
+}
+
+#endif /* unused */
+
+
+/**
+ * Checks whether a range of PDEs in the intermediate
+ * memory context are unused.
+ *
+ * We're talking 32-bit PDEs here.
+ *
+ * @returns true/false.
+ * @param pVM The cross context VM structure.
+ * @param iPD The first PDE in the range.
+ * @param cPTs The number of PDEs in the range.
+ */
+DECLINLINE(bool) pgmR3AreIntermediatePDEsUnused(PVM pVM, unsigned iPD, unsigned cPTs)
+{
+ if (pVM->pgm.s.pInterPD->a[iPD].n.u1Present)
+ return false;
+ while (cPTs > 1)
+ {
+ iPD++;
+ if (pVM->pgm.s.pInterPD->a[iPD].n.u1Present)
+ return false;
+ cPTs--;
+ }
+ return true;
+}
+
+
+/**
+ * Unlinks the mapping.
+ *
+ * The mapping *must* be in the list.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pMapping The mapping to unlink.
+ */
+static void pgmR3MapUnlink(PVM pVM, PPGMMAPPING pMapping)
+{
+ PPGMMAPPING pAfterThis = pVM->pgm.s.pMappingsR3;
+ if (pAfterThis == pMapping)
+ {
+ /* head */
+ pVM->pgm.s.pMappingsR3 = pMapping->pNextR3;
+ pVM->pgm.s.pMappingsRC = pMapping->pNextRC;
+ pVM->pgm.s.pMappingsR0 = pMapping->pNextR0;
+ }
+ else
+ {
+ /* in the list */
+ while (pAfterThis->pNextR3 != pMapping)
+ {
+ pAfterThis = pAfterThis->pNextR3;
+ AssertReleaseReturnVoid(pAfterThis);
+ }
+
+ pAfterThis->pNextR3 = pMapping->pNextR3;
+ pAfterThis->pNextRC = pMapping->pNextRC;
+ pAfterThis->pNextR0 = pMapping->pNextR0;
+ }
+}
+
+
+/**
+ * Links the mapping.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pMapping The mapping to linked.
+ */
+static void pgmR3MapLink(PVM pVM, PPGMMAPPING pMapping)
+{
+ /*
+ * Find the list location (it's sorted by GCPhys) and link it in.
+ */
+ if ( !pVM->pgm.s.pMappingsR3
+ || pVM->pgm.s.pMappingsR3->GCPtr > pMapping->GCPtr)
+ {
+ /* head */
+ pMapping->pNextR3 = pVM->pgm.s.pMappingsR3;
+ pMapping->pNextRC = pVM->pgm.s.pMappingsRC;
+ pMapping->pNextR0 = pVM->pgm.s.pMappingsR0;
+ pVM->pgm.s.pMappingsR3 = pMapping;
+ pVM->pgm.s.pMappingsRC = MMHyperR3ToRC(pVM, pMapping);
+ pVM->pgm.s.pMappingsR0 = MMHyperR3ToR0(pVM, pMapping);
+ }
+ else
+ {
+ /* in the list */
+ PPGMMAPPING pAfterThis = pVM->pgm.s.pMappingsR3;
+ PPGMMAPPING pBeforeThis = pAfterThis->pNextR3;
+ while (pBeforeThis && pBeforeThis->GCPtr <= pMapping->GCPtr)
+ {
+ pAfterThis = pBeforeThis;
+ pBeforeThis = pBeforeThis->pNextR3;
+ }
+
+ pMapping->pNextR3 = pAfterThis->pNextR3;
+ pMapping->pNextRC = pAfterThis->pNextRC;
+ pMapping->pNextR0 = pAfterThis->pNextR0;
+ pAfterThis->pNextR3 = pMapping;
+ pAfterThis->pNextRC = MMHyperR3ToRC(pVM, pMapping);
+ pAfterThis->pNextR0 = MMHyperR3ToR0(pVM, pMapping);
+ }
+}
+
+
+/**
+ * Finalizes the intermediate context.
+ *
+ * This is called at the end of the ring-3 init and will construct the
+ * intermediate paging structures, relocating all the mappings in the process.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @thread EMT(0)
+ */
+VMMR3DECL(int) PGMR3FinalizeMappings(PVM pVM)
+{
+ AssertReturn(!pVM->pgm.s.fFinalizedMappings, VERR_WRONG_ORDER);
+ pVM->pgm.s.fFinalizedMappings = true;
+
+ /*
+ * Loop until all mappings have been finalized.
+ */
+#if 0
+ unsigned iPDNext = UINT32_C(0xc0000000) >> X86_PD_SHIFT; /* makes CSAM/PATM freak out booting linux. :-/ */
+#elif 0
+ unsigned iPDNext = MM_HYPER_AREA_ADDRESS >> X86_PD_SHIFT;
+#else
+ unsigned iPDNext = 1 << X86_PD_SHIFT; /* no hint, map them from the top. */
+#endif
+
+ PPGMMAPPING pCur;
+ do
+ {
+ pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ if (!pCur->fFinalized)
+ {
+ /*
+ * Find a suitable location.
+ */
+ RTGCPTR const GCPtrOld = pCur->GCPtr;
+ const unsigned cPTs = pCur->cPTs;
+ unsigned iPDNew = iPDNext;
+ if ( iPDNew + cPTs >= X86_PG_ENTRIES /* exclude the last PD */
+ || !pgmR3AreIntermediatePDEsUnused(pVM, iPDNew, cPTs)
+ || !pCur->pfnRelocate(pVM, GCPtrOld, (RTGCPTR)iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_SUGGEST, pCur->pvUser))
+ {
+ /* No luck, just scan down from 4GB-4MB, giving up at 4MB. */
+ iPDNew = X86_PG_ENTRIES - cPTs - 1;
+ while ( iPDNew > 0
+ && ( !pgmR3AreIntermediatePDEsUnused(pVM, iPDNew, cPTs)
+ || !pCur->pfnRelocate(pVM, GCPtrOld, (RTGCPTR)iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_SUGGEST, pCur->pvUser))
+ )
+ iPDNew--;
+ AssertLogRelReturn(iPDNew != 0, VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+ }
+
+ /*
+ * Relocate it (something akin to pgmR3MapRelocate).
+ */
+ pgmR3MapSetPDEs(pVM, pCur, iPDNew);
+
+ /* unlink the mapping, update the entry and relink it. */
+ pgmR3MapUnlink(pVM, pCur);
+
+ RTGCPTR const GCPtrNew = (RTGCPTR)iPDNew << X86_PD_SHIFT;
+ pCur->GCPtr = GCPtrNew;
+ pCur->GCPtrLast = GCPtrNew + pCur->cb - 1;
+ pCur->fFinalized = true;
+
+ pgmR3MapLink(pVM, pCur);
+
+ /* Finally work the callback. */
+ pCur->pfnRelocate(pVM, GCPtrOld, GCPtrNew, PGMRELOCATECALL_RELOCATE, pCur->pvUser);
+
+ /*
+ * The list order might have changed, start from the beginning again.
+ */
+ iPDNext = iPDNew + cPTs;
+ break;
+ }
+
+ /* next */
+ pCur = pCur->pNextR3;
+ }
+ } while (pCur);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the size of the current guest mappings if they were to be
+ * put next to one another.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pcb Where to store the size.
+ */
+VMMR3DECL(int) PGMR3MappingsSize(PVM pVM, uint32_t *pcb)
+{
+ RTGCPTR cb = 0;
+#ifndef PGM_WITHOUT_MAPPINGS
+ for (PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3; pCur; pCur = pCur->pNextR3)
+ cb += pCur->cb;
+#else
+ RT_NOREF(pVM);
+#endif
+
+ *pcb = cb;
+ AssertReturn(*pcb == cb, VERR_NUMBER_TOO_BIG);
+ Log(("PGMR3MappingsSize: return %d (%#x) bytes\n", cb, cb));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Fixates the guest context mappings in a range reserved from the Guest OS.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtrBase The address of the reserved range of guest memory.
+ * @param cb The size of the range starting at GCPtrBase.
+ */
+VMMR3DECL(int) PGMR3MappingsFix(PVM pVM, RTGCPTR GCPtrBase, uint32_t cb)
+{
+ Log(("PGMR3MappingsFix: GCPtrBase=%RGv cb=%#x (fMappingsFixed=%RTbool MappingEnabled=%RTbool)\n",
+ GCPtrBase, cb, pVM->pgm.s.fMappingsFixed, pgmMapAreMappingsEnabled(pVM)));
+
+#ifndef PGM_WITHOUT_MAPPINGS
+ if (pgmMapAreMappingsEnabled(pVM))
+ {
+ /*
+ * Only applies to VCPU 0 as we don't support SMP guests with raw mode.
+ */
+ Assert(pVM->cCpus == 1);
+ PVMCPU pVCpu = &pVM->aCpus[0];
+
+ /*
+ * Before we do anything we'll do a forced PD sync to try make sure any
+ * pending relocations because of these mappings have been resolved.
+ */
+ PGMSyncCR3(pVCpu, CPUMGetGuestCR0(pVCpu), CPUMGetGuestCR3(pVCpu), CPUMGetGuestCR4(pVCpu), true);
+
+ return pgmR3MappingsFixInternal(pVM, GCPtrBase, cb);
+ }
+
+#else /* PGM_WITHOUT_MAPPINGS */
+ RT_NOREF(pVM, GCPtrBase, cb);
+#endif /* PGM_WITHOUT_MAPPINGS */
+
+ Assert(!VM_IS_RAW_MODE_ENABLED(pVM));
+ return VINF_SUCCESS;
+}
+
+
+#ifndef PGM_WITHOUT_MAPPINGS
+/**
+ * Internal worker for PGMR3MappingsFix and pgmR3Load.
+ *
+ * (This does not perform a SyncCR3 before the fixation like PGMR3MappingsFix.)
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param GCPtrBase The address of the reserved range of guest memory.
+ * @param cb The size of the range starting at GCPtrBase.
+ */
+int pgmR3MappingsFixInternal(PVM pVM, RTGCPTR GCPtrBase, uint32_t cb)
+{
+ /*
+ * Check input arguments and pre-conditions.
+ */
+ AssertMsgReturn(!(GCPtrBase & X86_PAGE_4M_OFFSET_MASK), ("GCPtrBase (%#x) has to be aligned on a 4MB address!\n", GCPtrBase),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(cb && !(cb & X86_PAGE_4M_OFFSET_MASK), ("cb (%#x) is 0 or not aligned on a 4MB address!\n", cb),
+ VERR_INVALID_PARAMETER);
+ AssertReturn(pgmMapAreMappingsEnabled(pVM), VERR_PGM_MAPPINGS_DISABLED);
+ AssertReturn(pVM->cCpus == 1, VERR_PGM_MAPPINGS_SMP);
+
+ /*
+ * Check that it's not conflicting with a core code mapping in the intermediate page table.
+ */
+ unsigned iPDNew = GCPtrBase >> X86_PD_SHIFT;
+ unsigned i = cb >> X86_PD_SHIFT;
+ while (i-- > 0)
+ {
+ if (pVM->pgm.s.pInterPD->a[iPDNew + i].n.u1Present)
+ {
+ /* Check that it's not one or our mappings. */
+ PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ if (iPDNew + i - (pCur->GCPtr >> X86_PD_SHIFT) < (pCur->cb >> X86_PD_SHIFT))
+ break;
+ pCur = pCur->pNextR3;
+ }
+ if (!pCur)
+ {
+ LogRel(("PGMR3MappingsFix: Conflicts with intermediate PDE %#x (GCPtrBase=%RGv cb=%#zx). The guest should retry.\n",
+ iPDNew + i, GCPtrBase, cb));
+ return VERR_PGM_MAPPINGS_FIX_CONFLICT;
+ }
+ }
+ }
+
+ /*
+ * In PAE / PAE mode, make sure we don't cross page directories.
+ */
+ PVMCPU pVCpu = &pVM->aCpus[0];
+ if ( ( pVCpu->pgm.s.enmGuestMode == PGMMODE_PAE
+ || pVCpu->pgm.s.enmGuestMode == PGMMODE_PAE_NX)
+ && ( pVCpu->pgm.s.enmShadowMode == PGMMODE_PAE
+ || pVCpu->pgm.s.enmShadowMode == PGMMODE_PAE_NX))
+ {
+ unsigned iPdptBase = GCPtrBase >> X86_PDPT_SHIFT;
+ unsigned iPdptLast = (GCPtrBase + cb - 1) >> X86_PDPT_SHIFT;
+ if (iPdptBase != iPdptLast)
+ {
+ LogRel(("PGMR3MappingsFix: Crosses PD boundary; iPdptBase=%#x iPdptLast=%#x (GCPtrBase=%RGv cb=%#zx). The guest should retry.\n",
+ iPdptBase, iPdptLast, GCPtrBase, cb));
+ return VERR_PGM_MAPPINGS_FIX_CONFLICT;
+ }
+ }
+
+ /*
+ * Loop the mappings and check that they all agree on their new locations.
+ */
+ RTGCPTR GCPtrCur = GCPtrBase;
+ PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ if (!pCur->pfnRelocate(pVM, pCur->GCPtr, GCPtrCur, PGMRELOCATECALL_SUGGEST, pCur->pvUser))
+ {
+ AssertMsgFailed(("The suggested fixed address %#x was rejected by '%s'!\n", GCPtrCur, pCur->pszDesc));
+ return VERR_PGM_MAPPINGS_FIX_REJECTED;
+ }
+ /* next */
+ GCPtrCur += pCur->cb;
+ pCur = pCur->pNextR3;
+ }
+ if (GCPtrCur > GCPtrBase + cb)
+ {
+ AssertMsgFailed(("cb (%#x) is less than the required range %#x!\n", cb, GCPtrCur - GCPtrBase));
+ return VERR_PGM_MAPPINGS_FIX_TOO_SMALL;
+ }
+
+ /*
+ * Loop the table assigning the mappings to the passed in memory
+ * and call their relocator callback.
+ */
+ GCPtrCur = GCPtrBase;
+ pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur)
+ {
+ RTGCPTR const GCPtrOld = pCur->GCPtr;
+
+ /*
+ * Relocate the page table(s).
+ */
+ if (pCur->GCPtr != NIL_RTGCPTR)
+ pgmR3MapClearPDEs(pVM, pCur, GCPtrOld >> X86_PD_SHIFT);
+ pgmR3MapSetPDEs(pVM, pCur, GCPtrCur >> X86_PD_SHIFT);
+
+ /*
+ * Update the entry.
+ */
+ pCur->GCPtr = GCPtrCur;
+ pCur->GCPtrLast = GCPtrCur + pCur->cb - 1;
+
+ /*
+ * Callback to execute the relocation.
+ */
+ pCur->pfnRelocate(pVM, GCPtrOld, GCPtrCur, PGMRELOCATECALL_RELOCATE, pCur->pvUser);
+
+ /*
+ * Advance.
+ */
+ GCPtrCur += pCur->cb;
+ pCur = pCur->pNextR3;
+ }
+
+ /*
+ * Mark the mappings as fixed at this new location and return.
+ */
+ pVM->pgm.s.fMappingsFixed = true;
+ pVM->pgm.s.fMappingsFixedRestored = false;
+ pVM->pgm.s.GCPtrMappingFixed = GCPtrBase;
+ pVM->pgm.s.cbMappingFixed = cb;
+
+ for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
+ {
+ pVM->aCpus[idCpu].pgm.s.fSyncFlags &= ~PGM_SYNC_MONITOR_CR3;
+ VMCPU_FF_SET(&pVM->aCpus[idCpu], VMCPU_FF_PGM_SYNC_CR3);
+ }
+ return VINF_SUCCESS;
+}
+#endif /*!PGM_WITHOUT_MAPPINGS*/
+
+
+/**
+ * Unfixes the mappings.
+ *
+ * Unless PGMR3MappingsDisable is in effect, mapping conflict detection will be
+ * enabled after this call. If the mappings are fixed, a full CR3 resync will
+ * take place afterwards.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+VMMR3DECL(int) PGMR3MappingsUnfix(PVM pVM)
+{
+ Log(("PGMR3MappingsUnfix: fMappingsFixed=%RTbool MappingsEnabled=%RTbool\n", pVM->pgm.s.fMappingsFixed, pgmMapAreMappingsEnabled(pVM)));
+ if ( pgmMapAreMappingsEnabled(pVM)
+ && ( pVM->pgm.s.fMappingsFixed
+ || pVM->pgm.s.fMappingsFixedRestored)
+ )
+ {
+ bool const fResyncCR3 = pVM->pgm.s.fMappingsFixed;
+
+ pVM->pgm.s.fMappingsFixed = false;
+ pVM->pgm.s.fMappingsFixedRestored = false;
+ pVM->pgm.s.GCPtrMappingFixed = 0;
+ pVM->pgm.s.cbMappingFixed = 0;
+
+ if (fResyncCR3)
+ for (VMCPUID i = 0; i < pVM->cCpus; i++)
+ VMCPU_FF_SET(&pVM->aCpus[i], VMCPU_FF_PGM_SYNC_CR3);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the mappings needs re-fixing after a restore.
+ *
+ * @returns true if they need, false if not.
+ * @param pVM The cross context VM structure.
+ */
+VMMR3DECL(bool) PGMR3MappingsNeedReFixing(PVM pVM)
+{
+ VM_ASSERT_VALID_EXT_RETURN(pVM, false);
+ return pVM->pgm.s.fMappingsFixedRestored;
+}
+
+#ifndef PGM_WITHOUT_MAPPINGS
+
+/**
+ * Map pages into the intermediate context (switcher code).
+ *
+ * These pages are mapped at both the give virtual address and at the physical
+ * address (for identity mapping).
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param Addr Intermediate context address of the mapping.
+ * @param HCPhys Start of the range of physical pages. This must be entriely below 4GB!
+ * @param cbPages Number of bytes to map.
+ *
+ * @remark This API shall not be used to anything but mapping the switcher code.
+ */
+VMMR3DECL(int) PGMR3MapIntermediate(PVM pVM, RTUINTPTR Addr, RTHCPHYS HCPhys, unsigned cbPages)
+{
+ LogFlow(("PGMR3MapIntermediate: Addr=%RTptr HCPhys=%RHp cbPages=%#x\n", Addr, HCPhys, cbPages));
+
+ /*
+ * Adjust input.
+ */
+ cbPages += (uint32_t)HCPhys & PAGE_OFFSET_MASK;
+ cbPages = RT_ALIGN(cbPages, PAGE_SIZE);
+ HCPhys &= X86_PTE_PAE_PG_MASK;
+ Addr &= PAGE_BASE_MASK;
+ /* We only care about the first 4GB, because on AMD64 we'll be repeating them all over the address space. */
+ uint32_t uAddress = (uint32_t)Addr;
+
+ /*
+ * Assert input and state.
+ */
+ AssertMsg(pVM->pgm.s.offVM, ("Bad init order\n"));
+ AssertMsg(pVM->pgm.s.pInterPD, ("Bad init order, paging.\n"));
+ AssertMsg(cbPages <= (512 << PAGE_SHIFT), ("The mapping is too big %d bytes\n", cbPages));
+ AssertMsg(HCPhys < _4G && HCPhys + cbPages < _4G, ("Addr=%RTptr HCPhys=%RHp cbPages=%d\n", Addr, HCPhys, cbPages));
+ AssertReturn(!pVM->pgm.s.fFinalizedMappings, VERR_WRONG_ORDER);
+
+ /*
+ * Check for internal conflicts between the virtual address and the physical address.
+ * A 1:1 mapping is fine, but partial overlapping is a no-no.
+ */
+ if ( uAddress != HCPhys
+ && ( uAddress < HCPhys
+ ? HCPhys - uAddress < cbPages
+ : uAddress - HCPhys < cbPages
+ )
+ )
+ AssertLogRelMsgFailedReturn(("Addr=%RTptr HCPhys=%RHp cbPages=%d\n", Addr, HCPhys, cbPages),
+ VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+
+ const unsigned cPages = cbPages >> PAGE_SHIFT;
+ int rc = pgmR3MapIntermediateCheckOne(pVM, uAddress, cPages, pVM->pgm.s.apInterPTs[0], pVM->pgm.s.apInterPaePTs[0]);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = pgmR3MapIntermediateCheckOne(pVM, (uintptr_t)HCPhys, cPages, pVM->pgm.s.apInterPTs[1], pVM->pgm.s.apInterPaePTs[1]);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Everythings fine, do the mapping.
+ */
+ pgmR3MapIntermediateDoOne(pVM, uAddress, HCPhys, cPages, pVM->pgm.s.apInterPTs[0], pVM->pgm.s.apInterPaePTs[0]);
+ pgmR3MapIntermediateDoOne(pVM, (uintptr_t)HCPhys, HCPhys, cPages, pVM->pgm.s.apInterPTs[1], pVM->pgm.s.apInterPaePTs[1]);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates that there are no conflicts for this mapping into the intermediate context.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param uAddress Address of the mapping.
+ * @param cPages Number of pages.
+ * @param pPTDefault Pointer to the default page table for this mapping.
+ * @param pPTPaeDefault Pointer to the default page table for this mapping.
+ */
+static int pgmR3MapIntermediateCheckOne(PVM pVM, uintptr_t uAddress, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault)
+{
+ AssertMsg((uAddress >> X86_PD_SHIFT) + cPages <= 1024, ("64-bit fixme uAddress=%RGv cPages=%u\n", uAddress, cPages));
+
+ /*
+ * Check that the ranges are available.
+ * (This code doesn't have to be fast.)
+ */
+ while (cPages > 0)
+ {
+ /*
+ * 32-Bit.
+ */
+ unsigned iPDE = (uAddress >> X86_PD_SHIFT) & X86_PD_MASK;
+ unsigned iPTE = (uAddress >> X86_PT_SHIFT) & X86_PT_MASK;
+ PX86PT pPT = pPTDefault;
+ if (pVM->pgm.s.pInterPD->a[iPDE].u)
+ {
+ RTHCPHYS HCPhysPT = pVM->pgm.s.pInterPD->a[iPDE].u & X86_PDE_PG_MASK;
+ if (HCPhysPT == MMPage2Phys(pVM, pVM->pgm.s.apInterPTs[0]))
+ pPT = pVM->pgm.s.apInterPTs[0];
+ else if (HCPhysPT == MMPage2Phys(pVM, pVM->pgm.s.apInterPTs[1]))
+ pPT = pVM->pgm.s.apInterPTs[1];
+ else
+ {
+ /** @todo this must be handled with a relocation of the conflicting mapping!
+ * Which of course cannot be done because we're in the middle of the initialization. bad design! */
+ AssertLogRelMsgFailedReturn(("Conflict between core code and PGMR3Mapping(). uAddress=%RHv\n", uAddress),
+ VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+ }
+ }
+ if (pPT->a[iPTE].u)
+ AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%RHv pPT->a[iPTE].u=%RX32\n", iPTE, iPDE, uAddress, pPT->a[iPTE].u),
+ VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+
+ /*
+ * PAE.
+ */
+ const unsigned iPDPE= (uAddress >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE;
+ iPDE = (uAddress >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
+ iPTE = (uAddress >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK;
+ Assert(iPDPE < 4);
+ Assert(pVM->pgm.s.apInterPaePDs[iPDPE]);
+ PX86PTPAE pPTPae = pPTPaeDefault;
+ if (pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u)
+ {
+ RTHCPHYS HCPhysPT = pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u & X86_PDE_PAE_PG_MASK;
+ if (HCPhysPT == MMPage2Phys(pVM, pVM->pgm.s.apInterPaePTs[0]))
+ pPTPae = pVM->pgm.s.apInterPaePTs[0];
+ else if (HCPhysPT == MMPage2Phys(pVM, pVM->pgm.s.apInterPaePTs[0]))
+ pPTPae = pVM->pgm.s.apInterPaePTs[1];
+ else
+ {
+ /** @todo this must be handled with a relocation of the conflicting mapping!
+ * Which of course cannot be done because we're in the middle of the initialization. bad design! */
+ AssertLogRelMsgFailedReturn(("Conflict between core code and PGMR3Mapping(). uAddress=%RHv\n", uAddress),
+ VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+ }
+ }
+ if (pPTPae->a[iPTE].u)
+ AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%RHv pPTPae->a[iPTE].u=%#RX64\n", iPTE, iPDE, uAddress, pPTPae->a[iPTE].u),
+ VERR_PGM_INTERMEDIATE_PAGING_CONFLICT);
+
+ /* next */
+ uAddress += PAGE_SIZE;
+ cPages--;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Sets up the intermediate page tables for a verified mapping.
+ *
+ * @param pVM The cross context VM structure.
+ * @param uAddress Address of the mapping.
+ * @param HCPhys The physical address of the page range.
+ * @param cPages Number of pages.
+ * @param pPTDefault Pointer to the default page table for this mapping.
+ * @param pPTPaeDefault Pointer to the default page table for this mapping.
+ */
+static void pgmR3MapIntermediateDoOne(PVM pVM, uintptr_t uAddress, RTHCPHYS HCPhys, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault)
+{
+ while (cPages > 0)
+ {
+ /*
+ * 32-Bit.
+ */
+ unsigned iPDE = (uAddress >> X86_PD_SHIFT) & X86_PD_MASK;
+ unsigned iPTE = (uAddress >> X86_PT_SHIFT) & X86_PT_MASK;
+ PX86PT pPT;
+ if (pVM->pgm.s.pInterPD->a[iPDE].u)
+ pPT = (PX86PT)MMPagePhys2Page(pVM, pVM->pgm.s.pInterPD->a[iPDE].u & X86_PDE_PG_MASK);
+ else
+ {
+ pVM->pgm.s.pInterPD->a[iPDE].u = X86_PDE_P | X86_PDE_A | X86_PDE_RW
+ | (uint32_t)MMPage2Phys(pVM, pPTDefault);
+ pPT = pPTDefault;
+ }
+ pPT->a[iPTE].u = X86_PTE_P | X86_PTE_RW | X86_PTE_A | X86_PTE_D | (uint32_t)HCPhys;
+
+ /*
+ * PAE
+ */
+ const unsigned iPDPE= (uAddress >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE;
+ iPDE = (uAddress >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
+ iPTE = (uAddress >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK;
+ Assert(iPDPE < 4);
+ Assert(pVM->pgm.s.apInterPaePDs[iPDPE]);
+ PX86PTPAE pPTPae;
+ if (pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u)
+ pPTPae = (PX86PTPAE)MMPagePhys2Page(pVM, pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u & X86_PDE_PAE_PG_MASK);
+ else
+ {
+ pPTPae = pPTPaeDefault;
+ pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u = X86_PDE_P | X86_PDE_A | X86_PDE_RW
+ | MMPage2Phys(pVM, pPTPaeDefault);
+ }
+ pPTPae->a[iPTE].u = X86_PTE_P | X86_PTE_RW | X86_PTE_A | X86_PTE_D | HCPhys;
+
+ /* next */
+ cPages--;
+ HCPhys += PAGE_SIZE;
+ uAddress += PAGE_SIZE;
+ }
+}
+
+
+/**
+ * Clears all PDEs involved with the mapping in the shadow and intermediate page tables.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pMap Pointer to the mapping in question.
+ * @param iOldPDE The index of the 32-bit PDE corresponding to the base of the mapping.
+ */
+static void pgmR3MapClearPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iOldPDE)
+{
+ unsigned i = pMap->cPTs;
+ PVMCPU pVCpu = VMMGetCpu(pVM);
+ pgmLock(pVM); /* to avoid assertions */
+
+ pgmMapClearShadowPDEs(pVM, pVCpu->pgm.s.CTX_SUFF(pShwPageCR3), pMap, iOldPDE, false /*fDeactivateCR3*/);
+
+ iOldPDE += i;
+ while (i-- > 0)
+ {
+ iOldPDE--;
+
+ /*
+ * 32-bit.
+ */
+ pVM->pgm.s.pInterPD->a[iOldPDE].u = 0;
+
+ /*
+ * PAE.
+ */
+ const unsigned iPD = iOldPDE / 256; /* iOldPDE * 2 / 512; iOldPDE is in 4 MB pages */
+ unsigned iPDE = iOldPDE * 2 % 512;
+ pVM->pgm.s.apInterPaePDs[iPD]->a[iPDE].u = 0;
+ iPDE++;
+ AssertFatal(iPDE < 512);
+ pVM->pgm.s.apInterPaePDs[iPD]->a[iPDE].u = 0;
+ }
+
+ pgmUnlock(pVM);
+}
+
+
+/**
+ * Sets all PDEs involved with the mapping in the shadow and intermediate page tables.
+ *
+ * @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.
+ */
+static void pgmR3MapSetPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iNewPDE)
+{
+ PPGM pPGM = &pVM->pgm.s;
+#ifdef VBOX_STRICT
+ PVMCPU pVCpu = VMMGetCpu(pVM);
+#endif
+ pgmLock(pVM); /* to avoid assertions */
+
+ Assert(!pgmMapAreMappingsEnabled(pVM) || PGMGetGuestMode(pVCpu) <= PGMMODE_PAE_NX);
+
+ pgmMapSetShadowPDEs(pVM, pMap, iNewPDE);
+
+ /*
+ * Init the page tables and insert them into the page directories.
+ */
+ unsigned i = pMap->cPTs;
+ iNewPDE += i;
+ while (i-- > 0)
+ {
+ iNewPDE--;
+
+ /*
+ * 32-bit.
+ */
+ X86PDE Pde;
+ /* Default mapping page directory flags are read/write and supervisor; individual page attributes determine the final flags */
+ Pde.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | (uint32_t)pMap->aPTs[i].HCPhysPT;
+ pPGM->pInterPD->a[iNewPDE] = Pde;
+
+ /*
+ * PAE.
+ */
+ const unsigned iPD = iNewPDE / 256;
+ unsigned iPDE = iNewPDE * 2 % 512;
+ X86PDEPAE PdePae0;
+ PdePae0.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT0;
+ pPGM->apInterPaePDs[iPD]->a[iPDE] = PdePae0;
+ iPDE++;
+ AssertFatal(iPDE < 512);
+ X86PDEPAE PdePae1;
+ PdePae1.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT1;
+ pPGM->apInterPaePDs[iPD]->a[iPDE] = PdePae1;
+ }
+
+ pgmUnlock(pVM);
+}
+
+
+/**
+ * Relocates a mapping to a new address.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pMapping The mapping to relocate.
+ * @param GCPtrOldMapping The address of the start of the old mapping.
+ * NIL_RTGCPTR if not currently mapped.
+ * @param GCPtrNewMapping The address of the start of the new mapping.
+ */
+static void pgmR3MapRelocate(PVM pVM, PPGMMAPPING pMapping, RTGCPTR GCPtrOldMapping, RTGCPTR GCPtrNewMapping)
+{
+ Log(("PGM: Relocating %s from %RGv to %RGv\n", pMapping->pszDesc, GCPtrOldMapping, GCPtrNewMapping));
+ AssertMsg(GCPtrOldMapping == pMapping->GCPtr, ("%RGv vs %RGv\n", GCPtrOldMapping, pMapping->GCPtr));
+ AssertMsg((GCPtrOldMapping >> X86_PD_SHIFT) < X86_PG_ENTRIES, ("%RGv\n", GCPtrOldMapping));
+ AssertMsg((GCPtrNewMapping >> X86_PD_SHIFT) < X86_PG_ENTRIES, ("%RGv\n", GCPtrOldMapping));
+
+ /*
+ * Relocate the page table(s).
+ */
+ if (GCPtrOldMapping != NIL_RTGCPTR)
+ pgmR3MapClearPDEs(pVM, pMapping, GCPtrOldMapping >> X86_PD_SHIFT);
+ pgmR3MapSetPDEs(pVM, pMapping, GCPtrNewMapping >> X86_PD_SHIFT);
+
+ /*
+ * Update and resort the mapping list.
+ */
+
+ /* Find previous mapping for pMapping, put result into pPrevMap. */
+ PPGMMAPPING pPrevMap = NULL;
+ PPGMMAPPING pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur && pCur != pMapping)
+ {
+ /* next */
+ pPrevMap = pCur;
+ pCur = pCur->pNextR3;
+ }
+ Assert(pCur);
+
+ /* Find mapping which >= than pMapping. */
+ RTGCPTR GCPtrNew = GCPtrNewMapping;
+ PPGMMAPPING pPrev = NULL;
+ pCur = pVM->pgm.s.pMappingsR3;
+ while (pCur && pCur->GCPtr < GCPtrNew)
+ {
+ /* next */
+ pPrev = pCur;
+ pCur = pCur->pNextR3;
+ }
+
+ if (pCur != pMapping && pPrev != pMapping)
+ {
+ /*
+ * Unlink.
+ */
+ if (pPrevMap)
+ {
+ pPrevMap->pNextR3 = pMapping->pNextR3;
+ pPrevMap->pNextRC = pMapping->pNextRC;
+ pPrevMap->pNextR0 = pMapping->pNextR0;
+ }
+ else
+ {
+ pVM->pgm.s.pMappingsR3 = pMapping->pNextR3;
+ pVM->pgm.s.pMappingsRC = pMapping->pNextRC;
+ pVM->pgm.s.pMappingsR0 = pMapping->pNextR0;
+ }
+
+ /*
+ * Link
+ */
+ pMapping->pNextR3 = pCur;
+ if (pPrev)
+ {
+ pMapping->pNextRC = pPrev->pNextRC;
+ pMapping->pNextR0 = pPrev->pNextR0;
+ pPrev->pNextR3 = pMapping;
+ pPrev->pNextRC = MMHyperR3ToRC(pVM, pMapping);
+ pPrev->pNextR0 = MMHyperR3ToR0(pVM, pMapping);
+ }
+ else
+ {
+ pMapping->pNextRC = pVM->pgm.s.pMappingsRC;
+ pMapping->pNextR0 = pVM->pgm.s.pMappingsR0;
+ pVM->pgm.s.pMappingsR3 = pMapping;
+ pVM->pgm.s.pMappingsRC = MMHyperR3ToRC(pVM, pMapping);
+ pVM->pgm.s.pMappingsR0 = MMHyperR3ToR0(pVM, pMapping);
+ }
+ }
+
+ /*
+ * Update the entry.
+ */
+ pMapping->GCPtr = GCPtrNew;
+ pMapping->GCPtrLast = GCPtrNew + pMapping->cb - 1;
+
+ /*
+ * Callback to execute the relocation.
+ */
+ pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_RELOCATE, pMapping->pvUser);
+}
+
+
+/**
+ * Checks if a new mapping address wasn't previously used and caused a clash with guest mappings.
+ *
+ * @returns VBox status code.
+ * @param pMapping The mapping which conflicts.
+ * @param GCPtr New mapping address to try
+ */
+bool pgmR3MapIsKnownConflictAddress(PPGMMAPPING pMapping, RTGCPTR GCPtr)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(pMapping->aGCPtrConflicts); i++)
+ {
+ if (GCPtr == pMapping->aGCPtrConflicts[i])
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Resolves a conflict between a page table based GC mapping and
+ * the Guest OS page tables. (32 bits version)
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pMapping The mapping which conflicts.
+ * @param pPDSrc The page directory of the guest OS.
+ * @param GCPtrOldMapping The address of the start of the current mapping.
+ */
+int pgmR3SyncPTResolveConflict(PVM pVM, PPGMMAPPING pMapping, PX86PD pPDSrc, RTGCPTR GCPtrOldMapping)
+{
+ STAM_REL_COUNTER_INC(&pVM->pgm.s.cRelocations);
+ STAM_PROFILE_START(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3ResolveConflict, a);
+
+ /* Raw mode only which implies one VCPU. */
+ Assert(pVM->cCpus == 1);
+
+ pMapping->aGCPtrConflicts[pMapping->cConflicts & (PGMMAPPING_CONFLICT_MAX-1)] = GCPtrOldMapping;
+ pMapping->cConflicts++;
+
+ /*
+ * Scan for free page directory entries.
+ *
+ * Note that we do not support mappings at the very end of the
+ * address space since that will break our GCPtrEnd assumptions.
+ */
+ const unsigned cPTs = pMapping->cPTs;
+ unsigned iPDNew = RT_ELEMENTS(pPDSrc->a) - cPTs; /* (+ 1 - 1) */
+ while (iPDNew-- > 0)
+ {
+ if (pPDSrc->a[iPDNew].n.u1Present)
+ continue;
+
+ if (pgmR3MapIsKnownConflictAddress(pMapping, iPDNew << X86_PD_SHIFT))
+ continue;
+
+ if (cPTs > 1)
+ {
+ bool fOk = true;
+ for (unsigned i = 1; fOk && i < cPTs; i++)
+ if (pPDSrc->a[iPDNew + i].n.u1Present)
+ fOk = false;
+ if (!fOk)
+ continue;
+ }
+
+ /*
+ * Check that it's not conflicting with an intermediate page table mapping.
+ */
+ bool fOk = true;
+ unsigned i = cPTs;
+ while (fOk && i-- > 0)
+ fOk = !pVM->pgm.s.pInterPD->a[iPDNew + i].n.u1Present;
+ if (!fOk)
+ continue;
+ /** @todo AMD64 should check the PAE directories and skip the 32bit stuff. */
+
+ /*
+ * Ask for the mapping.
+ */
+ RTGCPTR GCPtrNewMapping = (RTGCPTR32)iPDNew << X86_PD_SHIFT;
+
+ if (pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_SUGGEST, pMapping->pvUser))
+ {
+ pgmR3MapRelocate(pVM, pMapping, GCPtrOldMapping, GCPtrNewMapping);
+ STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3ResolveConflict, a);
+ return VINF_SUCCESS;
+ }
+ }
+
+ STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3ResolveConflict, a);
+#ifdef DEBUG_bird
+ /*
+ * Ended up here frequently recently with an NT4.0 VM (using SMP kernel).
+ *
+ * The problem is when enabling large pages (i.e. updating CR4) using the
+ * _Ki386EnableCurrentLargePage@8 assembly routine (address 0x801c97ad-9).
+ * The routine loads a sparsely popuplated page tables with identiy mappings
+ * of its own code, most entries are whatever ExAllocatePool returned, which
+ * is documented as undefined but all 0xffffffff in this case. Once loaded,
+ * it jumps to the physical code address, disables paging, set CR4.PSE=1,
+ * re-enables paging, restore the original page table and returns successfully.
+ *
+ * Theory: if CSAM/PATM patches the pushf;cli;mov eax,cr3; sequence, at the
+ * start of that function we're apparently in trouble, if CSAM/PATM doesn't
+ * we're switching back to REM and doing disabling of paging there instead.
+ *
+ * Normal PD: CR3=00030000; Problematic identity mapped PD: CR3=0x5fa000.
+ */
+ DBGFSTOP(pVM);
+#endif
+ AssertMsgFailed(("Failed to relocate page table mapping '%s' from %#x! (cPTs=%d)\n", pMapping->pszDesc, GCPtrOldMapping, cPTs));
+ return VERR_PGM_NO_HYPERVISOR_ADDRESS;
+}
+
+
+/**
+ * Resolves a conflict between a page table based GC mapping and
+ * the Guest OS page tables. (PAE bits version)
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pMapping The mapping which conflicts.
+ * @param GCPtrOldMapping The address of the start of the current mapping.
+ */
+int pgmR3SyncPTResolveConflictPAE(PVM pVM, PPGMMAPPING pMapping, RTGCPTR GCPtrOldMapping)
+{
+ STAM_REL_COUNTER_INC(&pVM->pgm.s.cRelocations);
+ STAM_PROFILE_START(&pVM->pgm.s.StatR3ResolveConflict, a);
+
+ /* Raw mode only which implies one VCPU. */
+ Assert(pVM->cCpus == 1);
+ PVMCPU pVCpu = VMMGetCpu(pVM);
+
+ pMapping->aGCPtrConflicts[pMapping->cConflicts & (PGMMAPPING_CONFLICT_MAX-1)] = GCPtrOldMapping;
+ pMapping->cConflicts++;
+
+ for (int iPDPTE = X86_PG_PAE_PDPE_ENTRIES - 1; iPDPTE >= 0; iPDPTE--)
+ {
+ unsigned iPDSrc;
+ PX86PDPAE pPDSrc = pgmGstGetPaePDPtr(pVCpu, (RTGCPTR32)iPDPTE << X86_PDPT_SHIFT, &iPDSrc, NULL);
+
+ /*
+ * Scan for free page directory entries.
+ *
+ * Note that we do not support mappings at the very end of the
+ * address space since that will break our GCPtrEnd assumptions.
+ * Nor do we support mappings crossing page directories.
+ */
+ const unsigned cPTs = pMapping->cb >> X86_PD_PAE_SHIFT;
+ unsigned iPDNew = RT_ELEMENTS(pPDSrc->a) - cPTs; /* (+ 1 - 1) */
+
+ while (iPDNew-- > 0)
+ {
+ /* Ugly assumption that mappings start on a 4 MB boundary. */
+ if (iPDNew & 1)
+ continue;
+
+ if (pgmR3MapIsKnownConflictAddress(pMapping, ((RTGCPTR32)iPDPTE << X86_PDPT_SHIFT) + (iPDNew << X86_PD_PAE_SHIFT)))
+ continue;
+
+ if (pPDSrc)
+ {
+ if (pPDSrc->a[iPDNew].n.u1Present)
+ continue;
+ if (cPTs > 1)
+ {
+ bool fOk = true;
+ for (unsigned i = 1; fOk && i < cPTs; i++)
+ if (pPDSrc->a[iPDNew + i].n.u1Present)
+ fOk = false;
+ if (!fOk)
+ continue;
+ }
+ }
+ /*
+ * Check that it's not conflicting with an intermediate page table mapping.
+ */
+ bool fOk = true;
+ unsigned i = cPTs;
+ while (fOk && i-- > 0)
+ fOk = !pVM->pgm.s.apInterPaePDs[iPDPTE]->a[iPDNew + i].n.u1Present;
+ if (!fOk)
+ continue;
+
+ /*
+ * Ask for the mapping.
+ */
+ RTGCPTR GCPtrNewMapping = ((RTGCPTR32)iPDPTE << X86_PDPT_SHIFT) + ((RTGCPTR32)iPDNew << X86_PD_PAE_SHIFT);
+
+ if (pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_SUGGEST, pMapping->pvUser))
+ {
+ pgmR3MapRelocate(pVM, pMapping, GCPtrOldMapping, GCPtrNewMapping);
+ STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3ResolveConflict, a);
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->StatR3ResolveConflict, a);
+ AssertMsgFailed(("Failed to relocate page table mapping '%s' from %#x! (cPTs=%d)\n", pMapping->pszDesc, GCPtrOldMapping, pMapping->cb >> X86_PD_PAE_SHIFT));
+ return VERR_PGM_NO_HYPERVISOR_ADDRESS;
+}
+
+#endif /* !PGM_WITHOUT_MAPPINGS */
+
+/**
+ * Read memory from the guest mappings.
+ *
+ * This will use the page tables associated with the mappings to
+ * read the memory. This means that not all kind of memory is readable
+ * since we don't necessarily know how to convert that physical address
+ * to a HC virtual one.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pvDst The destination address (HC of course).
+ * @param GCPtrSrc The source address (GC virtual address).
+ * @param cb Number of bytes to read.
+ *
+ * @remarks The is indirectly for DBGF only.
+ * @todo Consider renaming it to indicate it's special usage, or just
+ * reimplement it in MMR3HyperReadGCVirt.
+ */
+VMMR3DECL(int) PGMR3MapRead(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb)
+{
+ /*
+ * Simplicity over speed... Chop the request up into chunks
+ * which don't cross pages.
+ */
+ if (cb + (GCPtrSrc & PAGE_OFFSET_MASK) > PAGE_SIZE)
+ {
+ for (;;)
+ {
+ size_t cbRead = RT_MIN(cb, PAGE_SIZE - (GCPtrSrc & PAGE_OFFSET_MASK));
+ int rc = PGMR3MapRead(pVM, pvDst, GCPtrSrc, cbRead);
+ if (RT_FAILURE(rc))
+ return rc;
+ cb -= cbRead;
+ if (!cb)
+ break;
+ pvDst = (char *)pvDst + cbRead;
+ GCPtrSrc += cbRead;
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Find the mapping.
+ */
+ PPGMMAPPING pCur = pVM->pgm.s.CTX_SUFF(pMappings);
+ while (pCur)
+ {
+ RTGCPTR off = GCPtrSrc - pCur->GCPtr;
+ if (off < pCur->cb)
+ {
+ if (off + cb > pCur->cb)
+ {
+ AssertMsgFailed(("Invalid page range %RGv LB%#x. mapping '%s' %RGv to %RGv\n",
+ GCPtrSrc, cb, pCur->pszDesc, pCur->GCPtr, pCur->GCPtrLast));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ unsigned iPT = off >> X86_PD_SHIFT;
+ unsigned iPTE = (off >> PAGE_SHIFT) & X86_PT_MASK;
+ while (cb > 0 && iPTE < RT_ELEMENTS(CTXALLSUFF(pCur->aPTs[iPT].pPT)->a))
+ {
+ PCPGMSHWPTEPAE pPte = &pCur->aPTs[iPT].CTXALLSUFF(paPaePTs)[iPTE / 512].a[iPTE % 512];
+ if (!PGMSHWPTEPAE_IS_P(*pPte))
+ return VERR_PAGE_NOT_PRESENT;
+ RTHCPHYS HCPhys = PGMSHWPTEPAE_GET_HCPHYS(*pPte);
+
+ /*
+ * Get the virtual page from the physical one.
+ */
+ void *pvPage;
+ int rc = MMR3HCPhys2HCVirt(pVM, HCPhys, &pvPage);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ memcpy(pvDst, (char *)pvPage + (GCPtrSrc & PAGE_OFFSET_MASK), cb);
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* next */
+ pCur = CTXALLSUFF(pCur->pNext);
+ }
+
+ return VERR_INVALID_POINTER;
+}
+
+
+/**
+ * Info callback for 'pgmhandlers'.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pHlp The output helpers.
+ * @param pszArgs The arguments. phys or virt.
+ */
+DECLCALLBACK(void) pgmR3MapInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ NOREF(pszArgs);
+ if (!pgmMapAreMappingsEnabled(pVM))
+ pHlp->pfnPrintf(pHlp, "\nThe mappings are DISABLED.\n");
+ else if (pVM->pgm.s.fMappingsFixed)
+ pHlp->pfnPrintf(pHlp, "\nThe mappings are FIXED: %RGv-%RGv\n",
+ pVM->pgm.s.GCPtrMappingFixed, pVM->pgm.s.GCPtrMappingFixed + pVM->pgm.s.cbMappingFixed - 1);
+ else if (pVM->pgm.s.fMappingsFixedRestored)
+ pHlp->pfnPrintf(pHlp, "\nThe mappings are FLOATING-RESTORED-FIXED: %RGv-%RGv\n",
+ pVM->pgm.s.GCPtrMappingFixed, pVM->pgm.s.GCPtrMappingFixed + pVM->pgm.s.cbMappingFixed - 1);
+ else
+ pHlp->pfnPrintf(pHlp, "\nThe mappings are FLOATING.\n");
+
+ PPGMMAPPING pCur;
+ for (pCur = pVM->pgm.s.pMappingsR3; pCur; pCur = pCur->pNextR3)
+ {
+ pHlp->pfnPrintf(pHlp, "%RGv - %RGv %s\n", pCur->GCPtr, pCur->GCPtrLast, pCur->pszDesc);
+ if (pCur->cConflicts > 0)
+ {
+ pHlp->pfnPrintf(pHlp, " %u conflict%s: ", pCur->cConflicts, pCur->cConflicts == 1 ? "" : "s");
+ uint32_t cLeft = RT_MIN(pCur->cConflicts, RT_ELEMENTS(pCur->aGCPtrConflicts));
+ uint32_t i = pCur->cConflicts;
+ while (cLeft-- > 0)
+ {
+ i = (i - 1) & (PGMMAPPING_CONFLICT_MAX - 1);
+ pHlp->pfnPrintf(pHlp, cLeft ? "%RGv, " : "%RGv\n", pCur->aGCPtrConflicts[i]);
+ }
+ }
+ }
+}
+