PGMMap.cpp revision 7c9a5eca233baf6ede345ace077a00bd0b7af1ef
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * PGM - Page Manager, Guest Context Mappings.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Copyright (C) 2006-2007 Oracle Corporation
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * available from http://www.virtualbox.org. This file is free software;
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * you can redistribute it and/or modify it under the terms of the GNU
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * General Public License (GPL) as published by the Free Software
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync/*******************************************************************************
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync* Header Files *
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync*******************************************************************************/
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync/*******************************************************************************
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync* Internal Functions *
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync*******************************************************************************/
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsyncstatic void pgmR3MapClearPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iOldPDE);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsyncstatic void pgmR3MapSetPDEs(PVM pVM, PPGMMAPPING pMap, unsigned iNewPDE);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsyncstatic int pgmR3MapIntermediateCheckOne(PVM pVM, uintptr_t uAddress, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsyncstatic void pgmR3MapIntermediateDoOne(PVM pVM, uintptr_t uAddress, RTHCPHYS HCPhys, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Creates a page table based mapping in GC.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @returns VBox status code.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param pVM VM Handle.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param GCPtr Virtual Address. (Page table aligned!)
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param cb Size of the range. Must be a 4MB aligned!
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param fFlags PGMR3MAPPT_FLAGS_UNMAPPABLE or 0.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param pfnRelocate Relocation callback function.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param pvUser User argument to the callback.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * @param pszDesc Pointer to description string. This must not be freed.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsyncVMMR3DECL(int) PGMR3MapPT(PVM pVM, RTGCPTR GCPtr, uint32_t cb, uint32_t fFlags, PFNPGMRELOCATE pfnRelocate, void *pvUser, const char *pszDesc)
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync LogFlow(("PGMR3MapPT: GCPtr=%#x cb=%d fFlags=%#x pfnRelocate=%p pvUser=%p pszDesc=%s\n", GCPtr, cb, fFlags, pfnRelocate, pvUser, pszDesc));
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync AssertMsg(pVM->pgm.s.pInterPD, ("Paging isn't initialized, init order problems!\n"));
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Validate input.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync Assert(!fFlags || fFlags == PGMR3MAPPT_FLAGS_UNMAPPABLE);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync AssertMsgReturn(GCPtrLast >= GCPtr, ("Range wraps! GCPtr=%x GCPtrLast=%x\n", GCPtr, GCPtrLast),
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync AssertMsgReturn(!pVM->pgm.s.fMappingsFixed, ("Mappings are fixed! It's not possible to add new mappings at this time!\n"),
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync AssertPtrReturn(pfnRelocate, VERR_INVALID_PARAMETER);
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Find list location.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync if (pCur->GCPtrLast >= GCPtr && pCur->GCPtr <= GCPtrLast)
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync AssertMsgFailed(("Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync pCur->pszDesc, GCPtr, GCPtrLast, pCur->GCPtr, pCur->GCPtrLast));
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync LogRel(("VERR_PGM_MAPPING_CONFLICT: Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync pCur->pszDesc, GCPtr, GCPtrLast, pCur->GCPtr, pCur->GCPtrLast));
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync * Check for conflicts with intermediate mappings.
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync for (unsigned i = 0; i < cPTs; i++)
b63da7c87ee97d237d799dc5d275a70a546b5588vboxsync 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. */
int rc;
rc = MMHyperAlloc( pVM, RT_OFFSETOF(PGMMAPPING, aPTs[cPTs]), 0, MM_TAG_PGM_MAPPINGS, (void **)&pNew);
rc = MMR3HyperAllocOnceNoRel(pVM, RT_OFFSETOF(PGMMAPPING, aPTs[cPTs]), 0, MM_TAG_PGM_MAPPINGS, (void **)&pNew);
return rc;
rc = MMR3HyperAllocOnceNoRel(pVM, PAGE_SIZE * 3 * cPTs, PAGE_SIZE, MM_TAG_PGM_MAPPINGS, (void **)&pbPTs);
return VERR_NO_MEMORY;
for (unsigned i = 0; i < cPTs; i++)
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 (pPrev)
return VINF_SUCCESS;
while (pCur)
if (pPrev)
return VINF_SUCCESS;
return VERR_INVALID_PARAMETER;
iPD++;
cPTs--;
unsigned iPDNext = UINT32_C(0xc0000000) >> X86_PD_SHIFT; /* makes CSAM/PATM freak out booting linux. :-/ */
#elif 0
while (pCur)
|| !pCur->pfnRelocate(pVM, GCPtrOld, (RTGCPTR)iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_SUGGEST, pCur->pvUser))
while ( iPDNew > 0
|| !pCur->pfnRelocate(pVM, GCPtrOld, (RTGCPTR)iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_SUGGEST, pCur->pvUser))
iPDNew--;
} while (pCur);
return VINF_SUCCESS;
return VINF_SUCCESS;
Log(("PGMR3MappingsFix: GCPtrBase=%RGv cb=%#x (fMappingsFixed=%RTbool fMappingsDisabled=%RTbool)\n",
return VINF_SUCCESS;
AssertMsgReturn(!(GCPtrBase & X86_PAGE_4M_OFFSET_MASK), ("GCPtrBase (%#x) has to be aligned on a 4MB address!\n", GCPtrBase),
AssertMsgReturn(cb && !(cb & X86_PAGE_4M_OFFSET_MASK), ("cb (%#x) is 0 or not aligned on a 4MB address!\n", cb),
while (pCur)
if (!pCur)
LogRel(("PGMR3MappingsFix: Conflicts with intermediate PDE %#x (GCPtrBase=%RGv cb=%#zx). The guest should retry.\n",
return VERR_PGM_MAPPINGS_FIX_CONFLICT;
LogRel(("PGMR3MappingsFix: Crosses PD boundrary; iPdptBase=%#x iPdptLast=%#x (GCPtrBase=%RGv cb=%#zx). The guest should retry.\n",
return VERR_PGM_MAPPINGS_FIX_CONFLICT;
while (pCur)
AssertMsgFailed(("The suggested fixed address %#x was rejected by '%s'!\n", GCPtrCur, pCur->pszDesc));
return VERR_PGM_MAPPINGS_FIX_REJECTED;
return VERR_PGM_MAPPINGS_FIX_TOO_SMALL;
while (pCur)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
Log(("PGMR3MappingsUnfix: fMappingsFixed=%RTbool fMappingsDisabled=%RTbool\n", pVM->pgm.s.fMappingsFixed, pVM->pgm.s.fMappingsDisabled));
if (fResyncCR3)
return VINF_SUCCESS;
/* We only care about the first 4GB, because on AMD64 we'll be repeating them all over the address space. */
AssertMsg(HCPhys < _4G && HCPhys + cbPages < _4G, ("Addr=%RTptr HCPhys=%RHp cbPages=%d\n", Addr, HCPhys, cbPages));
int rc = pgmR3MapIntermediateCheckOne(pVM, uAddress, cPages, pVM->pgm.s.apInterPTs[0], pVM->pgm.s.apInterPaePTs[0]);
return rc;
rc = pgmR3MapIntermediateCheckOne(pVM, (uintptr_t)HCPhys, cPages, pVM->pgm.s.apInterPTs[1], pVM->pgm.s.apInterPaePTs[1]);
return rc;
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;
static int pgmR3MapIntermediateCheckOne(PVM pVM, uintptr_t uAddress, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault)
while (cPages > 0)
AssertLogRelMsgFailedReturn(("Conflict between core code and PGMR3Mapping(). uAddress=%RHv\n", uAddress),
AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%RHv pPT->a[iPTE].u=%RX32\n", iPTE, iPDE, uAddress, pPT->a[iPTE].u),
AssertLogRelMsgFailedReturn(("Conflict between core code and PGMR3Mapping(). uAddress=%RHv\n", uAddress),
AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%RHv pPTPae->a[iPTE].u=%#RX64\n", iPTE, iPDE, uAddress, pPTPae->a[iPTE].u),
cPages--;
return VINF_SUCCESS;
static void pgmR3MapIntermediateDoOne(PVM pVM, uintptr_t uAddress, RTHCPHYS HCPhys, unsigned cPages, PX86PT pPTDefault, PX86PTPAE pPTPaeDefault)
while (cPages > 0)
pPTPae = (PX86PTPAE)MMPagePhys2Page(pVM, pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u & X86_PDE_PAE_PG_MASK);
cPages--;
pgmMapClearShadowPDEs(pVM, pVCpu->pgm.s.CTX_SUFF(pShwPageCR3), pMap, iOldPDE, false /*fDeactivateCR3*/);
iOldPDE += i;
iOldPDE--;
iPDE++;
iNewPDE += i;
iNewPDE--;
/* 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;
PdePae0.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT0;
iPDE++;
PdePae1.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT1;
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));
if (pPrevMap)
if (pPrev)
pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_RELOCATE, pMapping->pvUser);
int pgmR3SyncPTResolveConflict(PVM pVM, PPGMMAPPING pMapping, PX86PD pPDSrc, RTGCPTR GCPtrOldMapping)
while (iPDNew-- > 0)
bool fOk = true;
fOk = false;
if (!fOk)
bool fOk = true;
unsigned i = cPTs;
while (fOk && i-- > 0)
if (!fOk)
if (pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_SUGGEST, pMapping->pvUser))
return VINF_SUCCESS;
AssertMsgFailed(("Failed to relocate page table mapping '%s' from %#x! (cPTs=%d)\n", pMapping->pszDesc, GCPtrOldMapping, cPTs));
return VERR_PGM_NO_HYPERVISOR_ADDRESS;
unsigned iPDSrc;
while (iPDNew-- > 0)
if (pgmR3MapIsKnownConflictAddress(pMapping, ((RTGCPTR32)iPDPTE << X86_PDPT_SHIFT) + (iPDNew << X86_PD_PAE_SHIFT)))
if (pPDSrc)
bool fOk = true;
fOk = false;
if (!fOk)
bool fOk = true;
unsigned i = cPTs;
while (fOk && i-- > 0)
if (!fOk)
RTGCPTR GCPtrNewMapping = ((RTGCPTR32)iPDPTE << X86_PDPT_SHIFT) + ((RTGCPTR32)iPDNew << X86_PD_PAE_SHIFT);
if (pMapping->pfnRelocate(pVM, GCPtrOldMapping, GCPtrNewMapping, PGMRELOCATECALL_SUGGEST, pMapping->pvUser))
return VINF_SUCCESS;
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;
return rc;
if (!cb)
return VINF_SUCCESS;
while (pCur)
return VERR_INVALID_PARAMETER;
return VERR_PAGE_NOT_PRESENT;
RTHCPHYS HCPhys = CTXALLSUFF(pCur->aPTs[iPT].paPaePTs)[iPTE / 512].a[iPTE % 512].u & X86_PTE_PAE_PG_MASK;
void *pvPage;
return rc;
return VINF_SUCCESS;
return VERR_INVALID_POINTER;
while (cLeft-- > 0)