PGMMap.cpp revision 2df4da7de0ceca3e5887164ad0738c1fb8e619f9
/* $Id$ */
/** @file
* PGM - Page Manager, Guest Context Mappings.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PGM
#include "PGMInternal.h"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
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);
/**
* Creates a page table based mapping in GC.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param GCPtr Virtual Address. (Page table aligned!)
* @param cb Size of the range. Must be a 4MB aligned!
* @param pfnRelocate Relocation callback function.
* @param pvUser User argument to the callback.
* @param pszDesc Pointer to description string. This must not be freed.
*/
PGMR3DECL(int) PGMR3MapPT(PVM pVM, RTGCPTR GCPtr, uint32_t cb, PFNPGMRELOCATE pfnRelocate, void *pvUser, const char *pszDesc)
{
LogFlow(("PGMR3MapPT: GCPtr=%#x cb=%d pfnRelocate=%p pvUser=%p pszDesc=%s\n", GCPtr, cb, pfnRelocate, pvUser, pszDesc));
AssertMsg(pVM->pgm.s.pInterPD && pVM->pgm.s.pHC32BitPD, ("Paging isn't initialized, init order problems!\n"));
/*
* Validate input.
*/
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
{
AssertMsgFailed(("Mappings are fixed! It's not possible to add new mappings at this time!\n"));
return VERR_PGM_MAPPINGS_FIXED;
}
if (!pfnRelocate)
{
AssertMsgFailed(("Callback is required\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Find list location.
*/
while (pCur)
{
{
AssertMsgFailed(("Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
LogRel(("VERR_PGM_MAPPING_CONFLICT: Address is already in use by %s. req %#x-%#x take %#x-%#x\n",
return VERR_PGM_MAPPING_CONFLICT;
}
break;
}
/*
* Check for conflicts with intermediate mappings.
*/
unsigned i;
for (i = 0; i < cPTs; i++)
{
{
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.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Allocate page tables and insert them into the page directories.
* (One 32-bit PT and two PAE PTs.)
*/
if (VBOX_FAILURE(rc))
{
return VERR_NO_MEMORY;
}
/*
* Init the page tables and insert them into the page directories.
*/
for (i = 0; i < cPTs; i++)
{
/*
* 32-bit.
*/
Log4(("PGMR3MapPT: i=%d: pPTHC=%p pPTGC=%p HCPhysPT=%RHp\n",
/*
* PAE.
*/
Log4(("PGMR3MapPT: i=%d: paPaePTsHC=%p paPaePTsGC=%p HCPhysPaePT0=%RHp HCPhysPaePT1=%RHp\n",
i, pNew->aPTs[i].paPaePTsR3, pNew->aPTs[i].paPaePTsGC, pNew->aPTs[i].HCPhysPaePT0, pNew->aPTs[i].HCPhysPaePT1));
}
/*
* Insert the new mapping.
*/
if (pPrev)
{
}
else
{
}
return VINF_SUCCESS;
}
/**
* Removes a page table based mapping.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param GCPtr Virtual Address. (Page table aligned!)
*/
{
/*
* Find it.
*/
while (pCur)
{
{
/*
* Unlink it.
*/
if (pPrev)
{
}
else
{
}
/*
* Free the page table memory, clear page directory entries
* and free the page tables and node memory.
*/
return VINF_SUCCESS;
}
/* done? */
break;
/* next */
}
return VERR_INVALID_PARAMETER;
}
/**
* Gets the size of the current guest mappings if they were to be
* put next to oneanother.
*
* @returns VBox status code.
* @param pVM The VM.
* @param pcb Where to store the size.
*/
{
RTGCUINTPTR cb = 0;
return VINF_SUCCESS;
}
/**
* Fixes the guest context mappings in a range reserved from the Guest OS.
*
* @returns VBox status code.
* @param pVM The VM.
* @param GCPtrBase The address of the reserved range of guest memory.
* @param cb The size of the range starting at GCPtrBase.
*/
{
/*
* This is all or nothing at all. So, a tiny bit of paranoia first.
*/
if (GCPtrBase & X86_PAGE_4M_OFFSET_MASK)
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
/*
* 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.
*/
/*
* Check that it's not conflicting with a core code mapping in the intermediate page table.
*/
unsigned i = cb >> X86_PD_SHIFT;
while (i-- > 0)
{
{
/* Check that it's not one or our mappings. */
while (pCur)
{
break;
}
if (!pCur)
{
LogRel(("PGMR3MappingsFix: Conflicts with intermediate PDE %#x (GCPtrBase=%VGv cb=%#zx). The guest should retry.\n",
return VERR_PGM_MAPPINGS_FIX_CONFLICT;
}
}
}
/*
* Loop the mappings and check that they all agree on their new locations.
*/
while (pCur)
{
{
AssertMsgFailed(("The suggested fixed address %#x was rejected by '%s'!\n", GCPtrCur, pCur->pszDesc));
return VERR_PGM_MAPPINGS_FIX_REJECTED;
}
/* next */
}
{
return VERR_PGM_MAPPINGS_FIX_TOO_SMALL;
}
/*
* Loop the table assigning the mappings to the passed in memory
* and call their relocator callback.
*/
while (pCur)
{
/*
* Relocate the page table(s).
*/
/*
* Update the entry.
*/
/*
* Callback to execute the relocation.
*/
pCur->pfnRelocate(pVM, iPDOld << X86_PD_SHIFT, iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_RELOCATE, pCur->pvUser);
/*
* Advance.
*/
}
/*
* Turn off CR3 updating monitoring.
*/
/*
* Mark the mappings as fixed and return.
*/
return VINF_SUCCESS;
}
/**
* Unfixes the mappings.
* After calling this function mapping conflict detection will be enabled.
*
* @returns VBox status code.
* @param pVM The VM.
*/
{
/*
* Re-enable the CR3 monitoring.
*
* Paranoia: We flush the page pool before doing that because Windows
* is using the CR3 page both as a PD and a PT, e.g. the pool may
* be monitoring it.
*/
#ifdef PGMPOOL_WITH_MONITORING
#endif
return VINF_SUCCESS;
}
/**
* 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 virtual machine.
* @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.
*/
{
/*
* Adjust input.
*/
Addr &= PAGE_BASE_MASK;
/* We only care about the first 4GB, because on AMD64 we'll be repeating them all over the address space. */
/*
* Assert input and state.
*/
AssertMsg(HCPhys < _4G && HCPhys + cbPages < _4G, ("Addr=%RTptr HCPhys=%VHp cbPages=%d\n", Addr, HCPhys, cbPages));
/*
* Check for internal conflicts between the virtual address and the physical address.
*/
)
)
/* The intermediate mapping must not conflict with our default hypervisor address. */
)
int rc = pgmR3MapIntermediateCheckOne(pVM, uAddress, cPages, pVM->pgm.s.apInterPTs[0], pVM->pgm.s.apInterPaePTs[0]);
if (VBOX_FAILURE(rc))
return rc;
rc = pgmR3MapIntermediateCheckOne(pVM, (uintptr_t)HCPhys, cPages, pVM->pgm.s.apInterPTs[1], pVM->pgm.s.apInterPaePTs[1]);
if (VBOX_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 VM handle.
* @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)
{
/*
* Check that the ranges are available.
* (This codes doesn't have to be fast.)
*/
while (cPages > 0)
{
/*
* 32-Bit.
*/
{
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=%VHv\n", uAddress),
}
}
AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%VHv pPT->a[iPTE].u=%RX32\n", iPTE, iPDE, uAddress, pPT->a[iPTE].u),
/*
* PAE.
*/
{
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=%VHv\n", uAddress),
}
}
AssertLogRelMsgFailedReturn(("Conflict iPTE=%#x iPDE=%#x uAddress=%VHv pPTPae->a[iPTE].u=%#RX64\n", iPTE, iPDE, uAddress, pPTPae->a[iPTE].u),
/* next */
cPages--;
}
return VINF_SUCCESS;
}
/**
* Sets up the intermediate page tables for a verified mapping.
*
* @param pVM VM handle.
* @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.
*/
else
{
pPT = pPTDefault;
}
/*
* PAE
*/
pPTPae = (PX86PTPAE)MMPagePhys2Page(pVM, pVM->pgm.s.apInterPaePDs[iPDPE]->a[iPDE].u & X86_PDE_PAE_PG_MASK);
else
{
}
/* next */
cPages--;
}
}
/**
* Clears all PDEs involved with the mapping.
*
* @param pPGM Pointer to the PGM instance data.
* @param pMap Pointer to the mapping in question.
* @param iOldPDE The index of the 32-bit PDE corresponding to the base of the mapping.
*/
{
iOldPDE += i;
while (i-- > 0)
{
iOldPDE--;
/*
* 32-bit.
*/
/*
* PAE.
*/
iPDE++;
/* Clear the PGM_PDFLAGS_MAPPING flag for the page directory pointer entry. (legacy PAE guest mode) */
}
}
/**
* Sets all PDEs involved with the mapping.
*
* @param pVM The VM handle.
* @param pMap Pointer to the mapping in question.
* @param iNewPDE The index of the 32-bit PDE corresponding to the base of the mapping.
*/
{
/* If mappings are not supposed to be put in the shadow page table, then this function is a nop. */
return;
/*
* Init the page tables and insert them into the page directories.
*/
iNewPDE += i;
while (i-- > 0)
{
iNewPDE--;
/*
* 32-bit.
*/
/* 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;
/*
* PAE.
*/
pgmPoolFree(pVM, pPGM->apHCPaePDs[iPD]->a[iPDE].u & X86_PDE_PAE_PG_MASK, PGMPOOL_IDX_PAE_PD, iNewPDE * 2);
PdePae0.u = PGM_PDFLAGS_MAPPING | X86_PDE_P | X86_PDE_A | X86_PDE_RW | X86_PDE_US | pMap->aPTs[i].HCPhysPaePT0;
iPDE++;
pgmPoolFree(pVM, pPGM->apHCPaePDs[iPD]->a[iPDE].u & X86_PDE_PAE_PG_MASK, PGMPOOL_IDX_PAE_PD, iNewPDE * 2 + 1);
PdePae1.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) */
}
}
/**
* Relocates a mapping to a new address.
*
* @param pVM VM handle.
* @param pMapping The mapping to relocate.
* @param GCPtrOldMapping The address of the start of the old mapping.
* @param GCPtrNewMapping The address of the start of the new mapping.
*/
void pgmR3MapRelocate(PVM pVM, PPGMMAPPING pMapping, RTGCPTR GCPtrOldMapping, RTGCPTR GCPtrNewMapping)
{
Log(("PGM: Relocating %s from %VGv to %VGv\n", pMapping->pszDesc, GCPtrOldMapping, GCPtrNewMapping));
/*
* Relocate the page table(s).
*/
/*
* Update and resort the mapping list.
*/
/* Find previous mapping for pMapping, put result into pPrevMap. */
{
/* next */
}
/* Find mapping which >= than pMapping. */
{
/* next */
}
{
/*
* Unlink.
*/
if (pPrevMap)
{
}
else
{
}
/*
* Link
*/
if (pPrev)
{
}
else
{
}
}
/*
* Update the entry.
*/
/*
* Callback to execute the relocation.
*/
pMapping->pfnRelocate(pVM, iPDOld << X86_PD_SHIFT, iPDNew << X86_PD_SHIFT, PGMRELOCATECALL_RELOCATE, pMapping->pvUser);
}
/**
* 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 VM Handle.
* @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)
{
/*
* 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.
*/
while (iPDNew-- > 0)
{
continue;
if (cPTs > 1)
{
bool fOk = true;
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)
if (!fOk)
continue;
/** @todo AMD64 should check the PAE directories and skip the 32bit stuff. */
/*
* Ask for the mapping.
*/
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;
}
/**
* 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 VM Handle.
* @param pMapping The mapping which conflicts.
* @param GCPtrOldMapping The address of the start of the current mapping.
*/
{
{
unsigned iPDSrc;
/*
* 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.
*/
while (iPDNew-- > 0)
{
/* Ugly assumption that mappings start on a 4 MB boundary. */
if (iPDNew & 1)
continue;
if (pPDSrc)
{
continue;
if (cPTs > 1)
{
bool fOk = true;
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)
if (!fOk)
continue;
/*
* Ask for the mapping.
*/
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;
}
/**
* Checks guest PD for conflicts with VMM GC mappings.
*
* @returns true if conflict detected.
* @returns false if not.
* @param pVM The virtual machine.
* @param cr3 Guest context CR3 register.
* @param fRawR0 Whether RawR0 is enabled or not.
*/
PGMR3DECL(bool) PGMR3MapHasConflicts(PVM pVM, uint64_t cr3, bool fRawR0) /** @todo how many HasConflict constructs do we really need? */
{
/*
* Can skip this if mappings are safely fixed.
*/
return false;
/*
* Iterate mappings.
*/
{
/*
* Resolve the page directory.
*/
{
while (iPT-- > 0)
{
Log(("PGMR3HasMappingConflicts: Conflict was detected at %VGv for mapping %s (32 bits)\n"
" iPDE=%#x iPT=%#x PDE=%VGp.\n",
return true;
}
}
}
else
{
{
while (iPT-- > 0)
{
{
Log(("PGMR3HasMappingConflicts: Conflict was detected at %VGv for mapping %s (PAE)\n"
" PDE=%VGp.\n",
return true;
}
}
}
}
else
AssertFailed();
return false;
}
/**
* 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.
* @param pVM VM handle.
* @param pvDst The destination address (HC of course).
* @param GCPtrSrc The source address (GC virtual address).
* @param cb Number of bytes to read.
*/
{
/** @todo remove this simplicity hack */
/*
* Simplicity over speed... Chop the request up into chunks
* which don't cross pages.
*/
{
for (;;)
{
if (VBOX_FAILURE(rc))
return rc;
if (!cb)
break;
}
return VINF_SUCCESS;
}
/*
* Find the mapping.
*/
while (pCur)
{
{
{
AssertMsgFailed(("Invalid page range %VGv LB%#x. mapping '%s' %VGv to %VGv\n",
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;
/*
* Get the virtual page from the physical one.
*/
void *pvPage;
if (VBOX_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
}
/* next */
}
return VERR_INVALID_POINTER;
}
/**
* Info callback for 'pgmhandlers'.
*
* @param pHlp The output helpers.
* @param pszArgs The arguments. phys or virt.
*/
{
? "\nThe mappings are FIXED.\n"
: "\nThe mappings are FLOATING.\n");
}