PGMPhys.cpp revision cc5ce86a395fb9d506d0520751ed61892a29317d
/* $Id$ */
/** @file
* PGM - Page Manager and Monitor, Physical Memory Addressing.
*/
/*
* 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_PHYS
#include "PGMInternal.h"
#include "PGMInline.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The number of pages to free in one batch. */
#define PGMPHYS_FREE_PAGE_BATCH_SIZE 128
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(int) pgmR3PhysRomWriteHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
static int pgmPhysFreePage(PVM pVM, PGMMFREEPAGESREQ pReq, uint32_t *pcPendingPages, PPGMPAGE pPage, RTGCPHYS GCPhys);
/*
* PGMR3PhysReadU8-64
* PGMR3PhysWriteU8-64
*/
#define PGMPHYSFN_READNAME PGMR3PhysReadU8
#define PGMPHYSFN_WRITENAME PGMR3PhysWriteU8
#define PGMPHYS_DATASIZE 1
#define PGMPHYS_DATATYPE uint8_t
#include "PGMPhysRWTmpl.h"
#define PGMPHYSFN_READNAME PGMR3PhysReadU16
#define PGMPHYSFN_WRITENAME PGMR3PhysWriteU16
#define PGMPHYS_DATASIZE 2
#define PGMPHYS_DATATYPE uint16_t
#include "PGMPhysRWTmpl.h"
#define PGMPHYSFN_READNAME PGMR3PhysReadU32
#define PGMPHYSFN_WRITENAME PGMR3PhysWriteU32
#define PGMPHYS_DATASIZE 4
#define PGMPHYS_DATATYPE uint32_t
#include "PGMPhysRWTmpl.h"
#define PGMPHYSFN_READNAME PGMR3PhysReadU64
#define PGMPHYSFN_WRITENAME PGMR3PhysWriteU64
#define PGMPHYS_DATASIZE 8
#define PGMPHYS_DATATYPE uint64_t
#include "PGMPhysRWTmpl.h"
/**
* EMT worker for PGMR3PhysReadExternal.
*/
static DECLCALLBACK(int) pgmR3PhysReadExternalEMT(PVM pVM, PRTGCPHYS pGCPhys, void *pvBuf, size_t cbRead)
{
return VINF_SUCCESS;
}
/**
* Write to physical memory, external users.
*
* @returns VBox status code.
* @retval VINF_SUCCESS.
*
* @param pVM VM Handle.
* @param GCPhys Physical address to write to.
* @param pvBuf What to write.
* @param cbWrite How many bytes to write.
*
* @thread Any but EMTs.
*/
{
/*
* Copy loop on ram ranges.
*/
for (;;)
{
/* Find range. */
/* Inside range or not? */
{
/*
* Must work our way thru this page by page.
*/
{
/*
* If the page has an ALL access handler, we'll have to
* delegate the job to EMT.
*/
{
}
/*
* Simple stuff, go ahead.
*/
const void *pvSrc;
if (RT_SUCCESS(rc))
else
{
AssertLogRelMsgFailed(("pgmPhysGCPhys2CCPtrInternalReadOnly failed on %RGp / %R[pgmpage] -> %Rrc\n",
}
/* next page */
{
return VINF_SUCCESS;
}
} /* walk pages in ram range. */
}
else
{
/*
* Unassigned address space.
*/
if (!pRam)
break;
{
break;
}
}
} /* Ram range walk */
return VINF_SUCCESS;
}
/**
* EMT worker for PGMR3PhysWriteExternal.
*/
static DECLCALLBACK(int) pgmR3PhysWriteExternalEMT(PVM pVM, PRTGCPHYS pGCPhys, const void *pvBuf, size_t cbWrite)
{
/** @todo VERR_EM_NO_MEMORY */
return VINF_SUCCESS;
}
/**
* Write to physical memory, external users.
*
* @returns VBox status code.
* @retval VINF_SUCCESS.
* @retval VERR_EM_NO_MEMORY.
*
* @param pVM VM Handle.
* @param GCPhys Physical address to write to.
* @param pvBuf What to write.
* @param cbWrite How many bytes to write.
* @param pszWho Who is writing. For tracking down who is writing
* after we've saved the state.
*
* @thread Any but EMTs.
*/
VMMDECL(int) PGMR3PhysWriteExternal(PVM pVM, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite, const char *pszWho)
{
("Calling PGMR3PhysWriteExternal after pgmR3Save()! GCPhys=%RGp cbWrite=%#x pszWho=%s\n",
/*
* Copy loop on ram ranges, stop when we hit something difficult.
*/
for (;;)
{
/* Find range. */
/* Inside range or not? */
{
/*
* Must work our way thru this page by page.
*/
{
/*
* Is the page problematic, we have to do the work on the EMT.
*
* Allocating writable pages and access handlers are
* problematic, write monitored pages are simple and can be
* dealth with here.
*/
{
else
{
}
}
/*
* Simple stuff, go ahead.
*/
void *pvDst;
if (RT_SUCCESS(rc))
else
AssertLogRelMsgFailed(("pgmPhysGCPhys2CCPtrInternal failed on %RGp / %R[pgmpage] -> %Rrc\n",
/* next page */
{
return VINF_SUCCESS;
}
} /* walk pages in ram range */
}
else
{
/*
* Unassigned address space, skip it.
*/
if (!pRam)
break;
break;
}
} /* Ram range walk */
return VINF_SUCCESS;
}
/**
* VMR3ReqCall worker for PGMR3PhysGCPhys2CCPtrExternal to make pages writable.
*
* @returns see PGMR3PhysGCPhys2CCPtrExternal
* @param pVM The VM handle.
* @param pGCPhys Pointer to the guest physical address.
* @param ppv Where to store the mapping address.
* @param pLock Where to store the lock.
*/
static DECLCALLBACK(int) pgmR3PhysGCPhys2CCPtrDelegated(PVM pVM, PRTGCPHYS pGCPhys, void **ppv, PPGMPAGEMAPLOCK pLock)
{
/*
* Just hand it to PGMPhysGCPhys2CCPtr and check that it's not a page with
* an access handler after it succeeds.
*/
if (RT_SUCCESS(rc))
{
if (PGM_PAGE_IS_MMIO(pPage))
{
}
else if ( PGM_PAGE_HAS_ACTIVE_HANDLERS(pPage)
#endif
)
{
/* We *must* flush any corresponding pgm pool page here, otherwise we'll
* not be informed about writes and keep bogus gst->shw mappings around.
*/
/** @todo r=bird: return VERR_PGM_PHYS_PAGE_RESERVED here if it still has
* active handlers, see the PGMR3PhysGCPhys2CCPtrExternal docs. */
}
}
return rc;
}
/**
* Requests the mapping of a guest page into ring-3, external threads.
*
* When you're done with the page, call PGMPhysReleasePageMappingLock() ASAP to
* release it.
*
* This API will assume your intention is to write to the page, and will
* therefore replace shared and zero pages. If you do not intend to modify the
* page, use the PGMR3PhysGCPhys2CCPtrReadOnlyExternal() API.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success.
* @retval VERR_PGM_PHYS_PAGE_RESERVED it it's a valid page but has no physical
* backing or if the page has any active access handlers. The caller
* must fall back on using PGMR3PhysWriteExternal.
* @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS if it's not a valid physical address.
*
* @param pVM The VM handle.
* @param GCPhys The guest physical address of the page that should be mapped.
* @param ppv Where to store the address corresponding to GCPhys.
* @param pLock Where to store the lock information that PGMPhysReleasePageMappingLock needs.
*
* @remark Avoid calling this API from within critical sections (other than the
* PGM one) because of the deadlock risk when we have to delegating the
* task to an EMT.
* @thread Any.
*/
VMMR3DECL(int) PGMR3PhysGCPhys2CCPtrExternal(PVM pVM, RTGCPHYS GCPhys, void **ppv, PPGMPAGEMAPLOCK pLock)
{
/*
* Query the Physical TLB entry for the page (may fail).
*/
if (RT_SUCCESS(rc))
{
if (PGM_PAGE_IS_MMIO(pPage))
else
{
/*
* If the page is shared, the zero page, or being write monitored
* it must be converted to an page that's writable if possible.
* We can only deal with write monitored pages here, the rest have
* to be on an EMT.
*/
#endif
)
{
#endif
)
else
{
}
}
/*
* Now, just perform the locking and calculate the return address.
*/
if (pMap)
{
if (cLocks == 0)
}
{
if (pMap)
}
}
}
return rc;
}
/**
* Requests the mapping of a guest page into ring-3, external threads.
*
* When you're done with the page, call PGMPhysReleasePageMappingLock() ASAP to
* release it.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success.
* @retval VERR_PGM_PHYS_PAGE_RESERVED it it's a valid page but has no physical
* backing or if the page as an active ALL access handler. The caller
* must fall back on using PGMPhysRead.
* @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS if it's not a valid physical address.
*
* @param pVM The VM handle.
* @param GCPhys The guest physical address of the page that should be mapped.
* @param ppv Where to store the address corresponding to GCPhys.
* @param pLock Where to store the lock information that PGMPhysReleasePageMappingLock needs.
*
* @remark Avoid calling this API from within critical sections (other than
* the PGM one) because of the deadlock risk.
* @thread Any.
*/
VMMR3DECL(int) PGMR3PhysGCPhys2CCPtrReadOnlyExternal(PVM pVM, RTGCPHYS GCPhys, void const **ppv, PPGMPAGEMAPLOCK pLock)
{
/*
* Query the Physical TLB entry for the page (may fail).
*/
if (RT_SUCCESS(rc))
{
#if 1
/* MMIO pages doesn't have any readable backing. */
if (PGM_PAGE_IS_MMIO(pPage))
#else
#endif
else
{
/*
* Now, just perform the locking and calculate the return address.
*/
if (pMap)
{
if (cLocks == 0)
}
{
AssertMsgFailed(("%RGp / %R[pgmpage] is entering permanent readonly locked state!\n", GCPhys, pPage));
if (pMap)
}
}
}
return rc;
}
/**
* Relinks the RAM ranges using the pSelfRC and pSelfR0 pointers.
*
* Called when anything was relocated.
*
* @param pVM Pointer to the shared VM structure.
*/
{
#ifdef VBOX_STRICT
{
}
#endif
if (pCur)
{
{
}
}
else
{
}
}
/**
* Links a new RAM range into the list.
*
* @param pVM Pointer to the shared VM structure.
* @param pNew Pointer to the new list entry.
* @param pPrev Pointer to the previous list entry. If NULL, insert as head.
*/
{
if (pPrev)
{
}
else
{
}
}
/**
* Unlink an existing RAM range from the list.
*
* @param pVM Pointer to the shared VM structure.
* @param pRam Pointer to the new list entry.
* @param pPrev Pointer to the previous list entry. If NULL, insert as head.
*/
{
if (pPrev)
{
}
else
{
}
}
/**
* Unlink an existing RAM range from the list.
*
* @param pVM Pointer to the shared VM structure.
* @param pRam Pointer to the new list entry.
*/
{
/* find prev. */
{
}
}
/**
* Frees a range of pages, replacing them with ZERO pages of the specified type.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param pRam The RAM range in which the pages resides.
* @param GCPhys The address of the first page.
* @param GCPhysLast The address of the last page.
* @param uType The page type to replace then with.
*/
static int pgmR3PhysFreePageRange(PVM pVM, PPGMRAMRANGE pRam, RTGCPHYS GCPhys, RTGCPHYS GCPhysLast, uint8_t uType)
{
uint32_t cPendingPages = 0;
/* Iterate the pages. */
while (cPagesLeft-- > 0)
{
pPageDst++;
}
if (cPendingPages)
{
}
return rc;
}
/**
* Rendezvous callback used by PGMR3ChangeMemBalloon that changes the memory balloon size
*
* This is only called on one of the EMTs while the other ones are waiting for
* it to complete this function.
*
* @returns VINF_SUCCESS (VBox strict status code).
* @param pVM The VM handle.
* @param pVCpu The VMCPU for the EMT we're being called on. Unused.
* @param pvUser User parameter
*/
static DECLCALLBACK(VBOXSTRICTRC) pgmR3PhysChangeMemBalloonRendezvous(PVM pVM, PVMCPU pVCpu, void *pvUser)
{
uint32_t cPendingPages = 0;
int rc;
if (fInflate)
{
/* Replace pages with ZERO pages. */
if (RT_FAILURE(rc))
{
return rc;
}
/* Iterate the pages. */
for (unsigned i = 0; i < cPages; i++)
{
{
Log(("PGMR3PhysFreePageRange: invalid physical page %RGp pPage->u3Type=%d\n", paPhysPage[i], (pPage) ? pPage->uTypeY : 0));
break;
}
if (RT_FAILURE(rc))
{
return rc;
}
}
if (cPendingPages)
{
if (RT_FAILURE(rc))
{
return rc;
}
}
/* Flush the PGM pool cache as we might have stale references to pages that we just freed. */
}
/* Notify GMM about the balloon change. */
rc = GMMR3BalloonedPages(pVM, (fInflate) ? GMMBALLOONACTION_INFLATE : GMMBALLOONACTION_DEFLATE, cPages);
if (RT_SUCCESS(rc))
{
if (!fInflate)
{
}
else
}
return rc;
}
/**
* Frees a range of ram pages, replacing them with ZERO pages; helper for PGMR3PhysFreeRamPages
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param fInflate Inflate or deflate memory balloon
* @param cPages Number of pages to free
* @param paPhysPage Array of guest physical addresses
*/
static DECLCALLBACK(void) pgmR3PhysChangeMemBalloonHelper(PVM pVM, bool fInflate, unsigned cPages, RTGCPHYS *paPhysPage)
{
int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, pgmR3PhysChangeMemBalloonRendezvous, (void *)paUser);
/* Made a copy in PGMR3PhysFreeRamPages; free it here. */
}
/**
* Inflate or deflate a memory balloon
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param fInflate Inflate or deflate memory balloon
* @param cPages Number of pages to free
* @param paPhysPage Array of guest physical addresses
*/
VMMR3DECL(int) PGMR3PhysChangeMemBalloon(PVM pVM, bool fInflate, unsigned cPages, RTGCPHYS *paPhysPage)
{
int rc;
/* We own the IOM lock here and could cause a deadlock by waiting for another VCPU that is blocking on the IOM lock.
* In the SMP case we post a request packet to postpone the job.
*/
{
rc = VMR3ReqCallNoWait(pVM, VMCPUID_ANY_QUEUE, (PFNRT)pgmR3PhysChangeMemBalloonHelper, 4, pVM, fInflate, cPages, paPhysPageCopy);
}
else
{
rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, pgmR3PhysChangeMemBalloonRendezvous, (void *)paUser);
}
return rc;
}
/**
* PGMR3PhysRegisterRam worker that initializes and links a RAM range.
*
* @param pVM The VM handle.
* @param pNew The new RAM range.
* @param GCPhys The address of the RAM range.
* @param GCPhysLast The last address of the RAM range.
* @param RCPtrNew The RC address if the range is floating. NIL_RTRCPTR
* if in HMA.
* @param R0PtrNew Ditto for R0.
* @param pszDesc The description.
* @param pPrev The previous RAM range (for linking).
*/
static void pgmR3PhysInitAndLinkRamRange(PVM pVM, PPGMRAMRANGE pNew, RTGCPHYS GCPhys, RTGCPHYS GCPhysLast,
{
/*
* Initialize the range.
*/
while (iPage-- > 0)
/* Update the page count stats. */
/*
* Link it.
*/
}
/**
* Relocate a floating RAM range.
*
* @copydoc FNPGMRELOCATE.
*/
static DECLCALLBACK(bool) pgmR3PhysRamRangeRelocate(PVM pVM, RTGCPTR GCPtrOld, RTGCPTR GCPtrNew, PGMRELOCATECALL enmMode, void *pvUser)
{
switch (enmMode)
{
case PGMRELOCATECALL_SUGGEST:
return true;
case PGMRELOCATECALL_RELOCATE:
{
/* Update myself and then relink all the ranges. */
return true;
}
default:
AssertFailedReturn(false);
}
}
/**
* PGMR3PhysRegisterRam worker that registers a high chunk.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param GCPhys The address of the RAM.
* @param cRamPages The number of RAM pages to register.
* @param cbChunk The size of the PGMRAMRANGE guest mapping.
* @param iChunk The chunk number.
* @param pszDesc The RAM range description.
*/
{
const char *pszDescChunk = iChunk == 0
? pszDesc
/*
* Allocate memory for the new chunk.
*/
size_t const cChunkPages = RT_ALIGN_Z(RT_UOFFSETOF(PGMRAMRANGE, aPages[cRamPages]), PAGE_SIZE) >> PAGE_SHIFT;
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
#else
NULL,
#endif
if (RT_SUCCESS(rc))
{
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
if (!VMMIsHwVirtExtForced(pVM))
#else
#endif
/*
* Create a mapping and map the pages into it.
* We push these in below the HMA.
*/
rc = PGMR3MapPT(pVM, GCPtrChunkMap, cbChunk, 0 /*fFlags*/, pgmR3PhysRamRangeRelocate, pNew, pszDescChunk);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Ok, init and link the range.
*/
}
}
if (RT_FAILURE(rc))
}
return rc;
}
/**
* Sets up a range RAM.
*
* This will check for conflicting registrations, make a resource
* reservation for the memory (with GMM), and setup the per-page
* tracking structures (PGMPAGE).
*
* @returns VBox stutus code.
* @param pVM Pointer to the shared VM structure.
* @param GCPhys The physical address of the RAM.
* @param cb The size of the RAM.
* @param pszDesc The description - not copied, so, don't free or change it.
*/
{
/*
* Validate input.
*/
AssertMsgReturn(GCPhysLast > GCPhys, ("The range wraps! GCPhys=%RGp cb=%RGp\n", GCPhys, cb), VERR_INVALID_PARAMETER);
/*
* Find range location and check for conflicts.
* (We don't lock here because the locking by EMT is only required on update.)
*/
{
AssertLogRelMsgFailedReturn(("%RGp-%RGp (%s) conflicts with existing %RGp-%RGp (%s)\n",
/* next */
}
/*
* Register it with GMM (the API bitches).
*/
if (RT_FAILURE(rc))
{
return rc;
}
&& cPages > 256)
{
/*
* The PGMRAMRANGE structures for the high memory can get very big.
* In order to avoid SUPR3PageAllocEx allocation failures due to the
* allocation size limit there and also to avoid being unable to find
* guest mapping space for them, we split this memory up into 4MB in
* mode.
*
* The first and last page of each mapping are guard pages and marked
* not-present. So, we've got 4186112 and 16769024 bytes available for
* the PGMRAMRANGE structure.
*
* Note! The sizes used here will influence the saved state.
*/
if (VMMIsHwVirtExtForced(pVM))
{
}
else
{
}
while (cPagesLeft > 0)
{
if (cPagesInChunk > cPagesPerChunk)
rc = pgmR3PhysRegisterHighRamChunk(pVM, GCPhysChunk, cPagesInChunk, cbChunk, iChunk, pszDesc, &pPrev);
/* advance */
iChunk++;
}
}
else
{
/*
* Allocate, initialize and link the new RAM range.
*/
pgmR3PhysInitAndLinkRamRange(pVM, pNew, GCPhys, GCPhysLast, NIL_RTRCPTR, NIL_RTR0PTR, pszDesc, pPrev);
}
/*
* Notify REM.
*/
return VINF_SUCCESS;
}
/**
* Worker called by PGMR3InitFinalize if we're configured to pre-allocate RAM.
*
* We do this late in the init process so that all the ROM and MMIO ranges have
* been registered already and we don't go wasting memory on them.
*
* @returns VBox status code.
*
* @param pVM Pointer to the shared VM structure.
*/
{
Log(("pgmR3PhysRamPreAllocate: enter\n"));
/*
* Walk the RAM ranges and allocate all RAM pages, halt at
* the first allocation error.
*/
{
while (cLeft-- > 0)
{
{
switch (PGM_PAGE_GET_STATE(pPage))
{
case PGM_PAGE_STATE_ZERO:
{
if (RT_FAILURE(rc))
{
LogRel(("PGM: RAM Pre-allocation failed at %RGp (in %s) with rc=%Rrc\n", GCPhys, pRam->pszDesc, rc));
return rc;
}
cPages++;
break;
}
case PGM_PAGE_STATE_ALLOCATED:
case PGM_PAGE_STATE_SHARED:
/* nothing to do here. */
break;
}
}
/* next */
pPage++;
}
}
Log(("pgmR3PhysRamPreAllocate: returns VINF_SUCCESS\n"));
return VINF_SUCCESS;
}
/**
* Resets (zeros) the RAM.
*
* ASSUMES that the caller owns the PGM lock.
*
* @returns VBox status code.
* @param pVM Pointer to the shared VM structure.
*/
{
/* Reset the memory balloon. */
/*
* We batch up pages that should be freed instead of calling GMM for
* each and every one of them.
*/
uint32_t cPendingPages = 0;
/*
* Walk the ram ranges.
*/
{
AssertMsg(((RTGCPHYS)iPage << PAGE_SHIFT) == pRam->cb, ("%RGp %RGp\n", (RTGCPHYS)iPage << PAGE_SHIFT, pRam->cb));
{
/* Replace all RAM pages by ZERO pages. */
while (iPage-- > 0)
{
switch (PGM_PAGE_GET_TYPE(pPage))
{
case PGMPAGETYPE_RAM:
/* Do not replace pages part of a 2 MB continuous range with zero pages, but zero them instead. */
{
void *pvPage;
}
else
if (!PGM_PAGE_IS_ZERO(pPage))
{
rc = pgmPhysFreePage(pVM, pReq, &cPendingPages, pPage, pRam->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT));
}
break;
break;
case PGMPAGETYPE_MMIO2:
case PGMPAGETYPE_ROM_SHADOW: /* handled by pgmR3PhysRomReset. */
case PGMPAGETYPE_ROM:
case PGMPAGETYPE_MMIO:
break;
default:
AssertFailed();
}
} /* for each page */
}
else
{
/* Zero the memory. */
while (iPage-- > 0)
{
switch (PGM_PAGE_GET_TYPE(pPage))
{
case PGMPAGETYPE_RAM:
switch (PGM_PAGE_GET_STATE(pPage))
{
case PGM_PAGE_STATE_ZERO:
break;
case PGM_PAGE_STATE_SHARED:
case PGM_PAGE_STATE_ALLOCATED:
{
void *pvPage;
break;
}
}
break;
break;
case PGMPAGETYPE_MMIO2:
case PGMPAGETYPE_ROM_SHADOW:
case PGMPAGETYPE_ROM:
case PGMPAGETYPE_MMIO:
break;
default:
AssertFailed();
}
} /* for each page */
}
}
/*
* Finish off any pages pending freeing.
*/
if (cPendingPages)
{
}
return VINF_SUCCESS;
}
/**
* This is the interface IOM is using to register an MMIO region.
*
* It will check for conflicts and ensure that a RAM range structure
* is present before calling the PGMR3HandlerPhysicalRegister API to
* register the callbacks.
*
* @returns VBox status code.
*
* @param pVM Pointer to the shared VM structure.
* @param GCPhys The start of the MMIO region.
* @param cb The size of the MMIO region.
* @param pfnHandlerR3 The address of the ring-3 handler. (IOMR3MMIOHandler)
* @param pvUserR3 The user argument for R3.
* @param pfnHandlerR0 The address of the ring-0 handler. (IOMMMIOHandler)
* @param pvUserR0 The user argument for R0.
* @param pfnHandlerRC The address of the RC handler. (IOMMMIOHandler)
* @param pvUserRC The user argument for RC.
* @param pszDesc The description of the MMIO region.
*/
{
/*
* Assert on some assumption.
*/
/*
* Make sure there's a RAM range structure for the region.
*/
int rc;
bool fRamExists = false;
{
{
/* Simplification: all within the same range. */
("%RGp-%RGp (MMIO/%s) falls partly outside %RGp-%RGp (%s)\n",
/* Check that it's all RAM or MMIO pages. */
while (cLeft-- > 0)
{
("%RGp-%RGp (MMIO/%s): %RGp is not a RAM or MMIO page - type=%d desc=%s\n",
pPage++;
}
/* Looks good. */
fRamExists = true;
break;
}
/* next */
}
if (fRamExists)
{
/*
* RAM pages currently mapped here. This might not be 100% correct
* for PCI memory, but we're doing the same thing for MMIO2 pages.
*/
if (RT_SUCCESS(rc))
{
}
}
else
{
/*
* No RAM range, insert an ad hoc one.
*
* Note that we don't have to tell REM about this range because
* PGMHandlerPhysicalRegisterEx will do that for us.
*/
Log(("PGMR3PhysMMIORegister: Adding ad hoc MMIO range for %RGp-%RGp %s\n", GCPhys, GCPhysLast, pszDesc));
rc = MMHyperAlloc(pVM, RT_OFFSETOF(PGMRAMRANGE, aPages[cPages]), 16, MM_TAG_PGM_PHYS, (void **)&pNew);
/* Initialize the range. */
while (iPage-- > 0)
/* update the page count stats. */
/* link it */
}
/*
* Register the access handler.
*/
if ( RT_FAILURE(rc)
&& !fRamExists)
{
/* remove the ad hoc range. */
}
return rc;
}
/**
* This is the interface IOM is using to register an MMIO region.
*
* It will take care of calling PGMHandlerPhysicalDeregister and clean up
* any ad hoc PGMRAMRANGE left behind.
*
* @returns VBox status code.
* @param pVM Pointer to the shared VM structure.
* @param GCPhys The start of the MMIO region.
* @param cb The size of the MMIO region.
*/
{
/*
* First deregister the handler, then check if we should remove the ram range.
*/
if (RT_SUCCESS(rc))
{
{
/** @todo We're being a bit too careful here. rewrite. */
{
/*
* See if all the pages are dead MMIO pages.
*/
bool fAllMMIO = true;
while (cLeft-- > 0)
{
/*|| not-out-of-action later */)
{
fAllMMIO = false;
break;
}
pPage++;
}
if (fAllMMIO)
{
/*
* Ad-hoc range, unlink and free it.
*/
Log(("PGMR3PhysMMIODeregister: Freeing ad hoc MMIO range for %RGp-%RGp %s\n",
break;
}
}
/*
* Range match? It will all be within one range (see PGMAllHandler.cpp).
*/
{
/*
* Turn the pages back into RAM pages.
*/
while (cLeft--)
{
AssertMsg(PGM_PAGE_IS_MMIO(pPage), ("%RGp %R[pgmpage]\n", pRam->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT), pPage));
AssertMsg(PGM_PAGE_IS_ZERO(pPage), ("%RGp %R[pgmpage]\n", pRam->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT), pPage));
}
break;
}
/* next */
}
}
return rc;
}
/**
* Locate a MMIO2 range.
*
* @returns Pointer to the MMIO2 range.
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The device instance owning the region.
* @param iRegion The region.
*/
{
/*
* Search the list.
*/
return pCur;
return NULL;
}
/**
* Allocate and register an MMIO2 region.
*
* As mentioned elsewhere, MMIO2 is just RAM spelled differently. It's
* RAM associated with a device. It is also non-shared memory with a
* permanent ring-3 mapping and page backing (presently).
*
* A MMIO2 range may overlap with base memory if a lot of RAM
* is configured for the VM, in which case we'll drop the base
* memory pages. Presently we will make no attempt to preserve
* anything that happens to be present in the base memory that
* is replaced, this is of course incorrectly but it's too much
* effort.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success, *ppv pointing to the R3 mapping of the memory.
* @retval VERR_ALREADY_EXISTS if the region already exists.
*
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The device instance owning the region.
* @param iRegion The region number. If the MMIO2 memory is a PCI I/O region
* this number has to be the number of that region. Otherwise
* it can be any number safe UINT8_MAX.
* @param cb The size of the region. Must be page aligned.
* @param fFlags Reserved for future use, must be zero.
* @param ppv Where to store the pointer to the ring-3 mapping of the memory.
* @param pszDesc The description.
*/
VMMR3DECL(int) PGMR3PhysMMIO2Register(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion, RTGCPHYS cb, uint32_t fFlags, void **ppv, const char *pszDesc)
{
/*
* Validate input.
*/
/*
* For the 2nd+ instance, mangle the description string so it's unique.
*/
{
if (!pszDesc)
return VERR_NO_MEMORY;
}
/*
* Try reserve and allocate the backing memory first as this is what is
* most likely to fail.
*/
if (RT_SUCCESS(rc))
{
void *pvPages;
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
/*
* Create the MMIO2 range record for it.
*/
if (RT_SUCCESS(rc))
{
//pNew->pNext = NULL;
//pNew->fMapped = false;
//pNew->fOverlapping = false;
//pNew->RamRange.paLSPages = NULL;
while (iPage-- > 0)
{
}
/* update page count stats */
/*
* Link it into the list.
* Since there is no particular order, just push it.
*/
return VINF_SUCCESS;
}
}
}
MMR3HeapFree((void *)pszDesc);
return rc;
}
/**
* Deregisters and frees an MMIO2 region.
*
* Any physical (and virtual) access handlers registered for the region must
* be deregistered before calling this function.
*
* @returns VBox status code.
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The device instance owning the region.
* @param iRegion The region. If it's UINT32_MAX it'll be a wildcard match.
*/
{
/*
* Validate input.
*/
int rc = VINF_SUCCESS;
unsigned cFound = 0;
while (pCur)
{
&& ( iRegion == UINT32_MAX
{
cFound++;
/*
* Unmap it if it's mapped.
*/
{
}
/*
* Unlink it
*/
if (pPrev)
else
/*
* Free the memory.
*/
/* we're leaking hyper memory here if done at runtime. */
#ifdef VBOX_STRICT
|| enmState == VMSTATE_OFF
|| enmState == VMSTATE_OFF_LS
|| enmState == VMSTATE_DESTROYING
|| enmState == VMSTATE_TERMINATED
|| enmState == VMSTATE_CREATING
#endif
/*rc = MMHyperFree(pVM, pCur);
AssertRCReturn(rc, rc); - not safe, see the alloc call. */
/* update page count stats */
/* next */
}
else
{
}
}
}
/**
* Maps a MMIO2 region.
*
* This is done when a guest / the bios / state loading changes the
* PCI config. The replacing of base memory has the same restrictions
* as during registration, of course.
*
* @returns VBox status code.
*
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The
*/
{
/*
* Validate input
*/
/*
* Find our location in the ram range list, checking for
* restriction we don't bother implementing yet (partially overlapping).
*/
bool fRamExists = false;
{
{
/* completely within? */
("%RGp-%RGp (MMIO2/%s) falls partly outside %RGp-%RGp (%s)\n",
fRamExists = true;
break;
}
/* next */
}
if (fRamExists)
{
while (cPagesLeft-- > 0)
{
("%RGp isn't a RAM page (%d) - mapping %RGp-%RGp (MMIO2/%s).\n",
pPage++;
}
}
Log(("PGMR3PhysMMIO2Map: %RGp-%RGp fRamExists=%RTbool %s\n",
/*
* Make the changes.
*/
if (fRamExists)
{
/** @todo use pgmR3PhysFreePageRange here. */
uint32_t cPendingPages = 0;
/* replace the pages, freeing all present RAM pages. */
while (cPagesLeft-- > 0)
{
pPageSrc++;
pPageDst++;
}
/* Flush physical page map TLB. */
if (cPendingPages)
{
}
}
else
{
/* link in the ram range */
}
return VINF_SUCCESS;
}
/**
* Unmaps a MMIO2 region.
*
* This is done when a guest / the bios / state loading changes the
* PCI config. The replacing of base memory has the same restrictions
* as during registration, of course.
*/
{
/*
* Validate input
*/
Log(("PGMR3PhysMMIO2Unmap: %RGp-%RGp %s\n",
/*
* Unmap it.
*/
bool fInformREM;
if (pCur->fOverlapping)
{
/* Restore the RAM pages we've replaced. */
while (cPagesLeft-- > 0)
{
pPageDst++;
}
/* Flush physical page map TLB. */
fInformREM = false;
}
else
{
fInformREM = true;
}
pCur->fOverlapping = false;
if (fInformREM)
return VINF_SUCCESS;
}
/**
* Checks if the given address is an MMIO2 base address or not.
*
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The owner of the memory, optional.
* @param GCPhys The address to check.
*/
{
/*
* Validate input
*/
VM_ASSERT_EMT_RETURN(pVM, false);
AssertPtrReturn(pDevIns, false);
AssertReturn(GCPhys != 0, false);
/*
* Search the list.
*/
{
return true;
}
return false;
}
/**
* Gets the HC physical address of a page in the MMIO2 region.
*
* This is API is intended for MMHyper and shouldn't be called
* by anyone else...
*
* @returns VBox status code.
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The owner of the memory, optional.
* @param iRegion The region.
* @param off The page expressed an offset into the MMIO2 region.
* @param pHCPhys Where to store the result.
*/
VMMR3DECL(int) PGMR3PhysMMIO2GetHCPhys(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion, RTGCPHYS off, PRTHCPHYS pHCPhys)
{
/*
* Validate input
*/
return VINF_SUCCESS;
}
/**
* Maps a portion of an MMIO2 region into kernel space (host).
*
* The kernel mapping will become invalid when the MMIO2 memory is deregistered
* or the VM is terminated.
*
* @return VBox status code.
*
* @param pVM Pointer to the shared VM structure.
* @param pDevIns The device owning the MMIO2 memory.
* @param iRegion The region.
* @param off The offset into the region. Must be page aligned.
* @param cb The number of bytes to map. Must be page aligned.
* @param pszDesc Mapping description.
* @param pR0Ptr Where to store the R0 address.
*/
VMMR3DECL(int) PGMR3PhysMMIO2MapKernel(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion, RTGCPHYS off, RTGCPHYS cb,
{
/*
* Validate input.
*/
/*
*/
return rc;
}
/**
* Registers a ROM image.
*
* Shadowed ROM images requires double the amount of backing memory, so,
* don't use that unless you have to. Shadowing of ROM images is process
* where we can select where the reads go and where the writes go. On real
* hardware the chipset provides means to configure this. We provide
* PGMR3PhysProtectROM() for this purpose.
*
* A read-only copy of the ROM image will always be kept around while we
* will allocate RAM pages for the changes on demand (unless all memory
* is configured to be preallocated).
*
* @returns VBox status.
* @param pVM VM Handle.
* @param pDevIns The device instance owning the ROM.
* @param GCPhys First physical address in the range.
* Must be page aligned!
* @param cbRange The size of the range (in bytes).
* Must be page aligned!
* @param pvBinary Pointer to the binary data backing the ROM image.
* This must be exactly \a cbRange in size.
* @param fFlags Mask of flags. PGMPHYS_ROM_FLAGS_SHADOWED
* @param pszDesc Pointer to description string. This must not be freed.
*
* @remark There is no way to remove the rom, automatically on device cleanup or
* manually from the device yet. This isn't difficult in any way, it's
* just not something we expect to be necessary for a while.
*/
{
Log(("PGMR3PhysRomRegister: pDevIns=%p GCPhys=%RGp(-%RGp) cb=%RGp pvBinary=%p fFlags=%#x pszDesc=%s\n",
/*
* Validate input.
*/
AssertReturn(!(fFlags & ~(PGMPHYS_ROM_FLAGS_SHADOWED | PGMPHYS_ROM_FLAGS_PERMANENT_BINARY)), VERR_INVALID_PARAMETER);
/*
* Find the ROM location in the ROM list first.
*/
{
AssertLogRelMsgFailedReturn(("%RGp-%RGp (%s) conflicts with existing %RGp-%RGp (%s)\n",
/* next */
}
/*
* Find the RAM location and check for conflicts.
*
* Conflict detection is a bit different than for RAM
* registration since a ROM can be located within a RAM
* range. So, what we have to check for is other memory
* types (other than RAM that is) and that we don't span
* more than one RAM range (layz).
*/
bool fRamExists = false;
{
{
/* completely within? */
("%RGp-%RGp (%s) falls partly outside %RGp-%RGp (%s)\n",
fRamExists = true;
break;
}
/* next */
}
if (fRamExists)
{
while (cPagesLeft-- > 0)
{
("%RGp (%R[pgmpage]) isn't a RAM page - registering %RGp-%RGp (%s).\n",
pPage++;
}
}
/*
* Update the base memory reservation if necessary.
*/
if (fFlags & PGMPHYS_ROM_FLAGS_SHADOWED)
cExtraBaseCost += cPages;
if (cExtraBaseCost)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Allocate memory for the virgin copy of the RAM.
*/
{
}
if (RT_FAILURE(rc))
{
return rc;
}
/*
* Allocate the new ROM range and RAM range (if necessary).
*/
rc = MMHyperAlloc(pVM, RT_OFFSETOF(PGMROMRANGE, aPages[cPages]), 0, MM_TAG_PGM_PHYS, (void **)&pRomNew);
if (RT_SUCCESS(rc))
{
if (!fRamExists)
rc = MMHyperAlloc(pVM, RT_OFFSETOF(PGMRAMRANGE, aPages[cPages]), sizeof(PGMPAGE), MM_TAG_PGM_PHYS, (void **)&pRamNew);
if (RT_SUCCESS(rc))
{
/*
* Initialize and insert the RAM range (if required).
*/
if (!fRamExists)
{
{
}
}
else
{
{
}
}
/* Flush physical page map TLB. */
/*
* !HACK ALERT! REM + (Shadowed) ROM ==> mess.
*
* If it's shadowed we'll register the handler after the ROM notification
* so we get the access handler callbacks that we should. If it isn't
* shadowed we'll do it the other way around to make REM use the built-in
* ROM behavior and not the handler behavior (which is to route all access
* to PGM atm).
*/
if (fFlags & PGMPHYS_ROM_FLAGS_SHADOWED)
{
}
else
{
}
if (RT_SUCCESS(rc))
{
/*
* Copy the image over to the virgin pages.
* This must be done after linking in the RAM range.
*/
{
void *pvDstPage;
if (RT_FAILURE(rc))
{
break;
}
}
if (RT_SUCCESS(rc))
{
/*
* Initialize the ROM range.
* Note that the Virgin member of the pages has already been initialized above.
*/
{
}
/* update the page count stats for the shadow pages. */
if (fFlags & PGMPHYS_ROM_FLAGS_SHADOWED)
{
}
/*
* Insert the ROM range, tell REM and return successfully.
*/
if (pRomPrev)
{
}
else
{
}
return VINF_SUCCESS;
}
/* bail out */
}
if (!fRamExists)
{
}
}
}
/** @todo Purge the mapping cache or something... */
return rc;
}
/**
* \#PF Handler callback for ROM write accesses.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPhys The physical address the guest is writing to.
* @param pvPhys The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) pgmR3PhysRomWriteHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
Log5(("pgmR3PhysRomWriteHandler: %d %c %#08RGp %#04zx\n", pRomPage->enmProt, enmAccessType == PGMACCESSTYPE_READ ? 'R' : 'W', GCPhys, cbBuf));
if (enmAccessType == PGMACCESSTYPE_READ)
{
{
/*
* Take the default action.
*/
return VINF_PGM_HANDLER_DO_DEFAULT;
default:
AssertMsgFailedReturn(("enmProt=%d iPage=%d GCPhys=%RGp\n",
}
}
else
{
{
/*
* Ignore writes.
*/
return VINF_SUCCESS;
/*
* Write to the ram page.
*/
case PGMROMPROT_READ_RAM_WRITE_RAM: /* yes this will get here too, it's *way* simpler that way. */
{
/* This should be impossible now, pvPhys doesn't work cross page anylonger. */
/*
* Take the lock, do lazy allocation, map the page and copy the data.
*
* Note that we have to bypass the mapping TLB since it works on
* guest physical addresses and entering the shadow page would
* kind of screw things up...
*/
{
}
void *pvDstPage;
if (RT_SUCCESS(rc))
{
}
return rc;
}
default:
AssertMsgFailedReturn(("enmProt=%d iPage=%d GCPhys=%RGp\n",
}
}
}
/**
* Called by PGMR3Reset to reset the shadow, switch to the virgin,
* and verify that the virgin part is untouched.
*
* This is done after the normal memory has been cleared.
*
* ASSUMES that the caller owns the PGM lock.
*
* @param pVM The VM handle.
*/
{
{
{
/*
* Reset the physical handler.
*/
/*
* What we do with the shadow pages depends on the memory
* preallocation option. If not enabled, we'll just throw
* out all the dirty pages and replace them by the zero page.
*/
{
/* Free the dirty pages. */
uint32_t cPendingPages = 0;
{
rc = pgmPhysFreePage(pVM, pReq, &cPendingPages, &pRom->aPages[iPage].Shadow, pRom->GCPhys + (iPage << PAGE_SHIFT));
}
if (cPendingPages)
{
}
}
else
{
/* clear all the shadow pages. */
{
void *pvDstPage;
if (RT_FAILURE(rc))
break;
}
}
}
#ifdef VBOX_STRICT
/*
* Verify that the virgin page is unchanged if possible.
*/
if (pRom->pvOriginal)
{
{
void const *pvDstPage;
if (RT_FAILURE(rc))
break;
LogRel(("pgmR3PhysRomReset: %RGp rom page changed (%s) - loaded saved state?\n",
}
}
#endif
}
return VINF_SUCCESS;
}
/**
* Change the shadowing of a range of ROM pages.
*
* This is intended for implementing chipset specific memory registers
* and will not be very strict about the input. It will silently ignore
* any pages that are not the part of a shadowed ROM.
*
* @returns VBox status code.
* @retval VINF_PGM_SYNC_CR3
*
* @param pVM Pointer to the shared VM structure.
* @param GCPhys Where to start. Page aligned.
* @param cb How much to change. Page aligned.
* @param enmProt The new ROM protection.
*/
{
/*
* Check input
*/
if (!cb)
return VINF_SUCCESS;
/*
* Process the request.
*/
int rc = VINF_SUCCESS;
bool fFlushTLB = false;
{
{
/*
* Iterate the relevant pages and make necessary the changes.
*/
bool fChanges = false;
iPage++)
{
{
fChanges = true;
/* flush references to the page. */
/** @todo preserve the volatile flags (handlers) when these have been moved out of HCPhys! */
}
}
/*
* Reset the access handler if we made changes, no need
* to optimize this.
*/
if (fChanges)
{
if (RT_FAILURE(rc2))
{
return rc2;
}
}
/* Advance - cb isn't updated. */
}
}
if (fFlushTLB)
return rc;
}
/**
* Sets the Address Gate 20 state.
*
* @param pVCpu The VCPU to operate on.
* @param fEnable True if the gate should be enabled.
* False if the gate should be disabled.
*/
{
{
/** @todo we're not handling this correctly for VT-x / AMD-V. See #2911 */
}
}
/**
* Tree enumeration callback for dealing with age rollover.
* It will perform a simple compression of the current age.
*/
{
/* Age compression - ASSUMES iNow == 4. */
else /* iAge = 0 */
/* reinsert */
return 0;
}
/**
* Tree enumeration callback that updates the chunks that have
* been used since the last
*/
{
{
}
return 0;
}
/**
* Performs ageing of the ring-3 chunk mappings.
*
* @param pVM The VM handle.
*/
{
{
RTAvlU32DoWithAll(&pVM->pgm.s.ChunkR3Map.pTree, true /*fFromLeft*/, pgmR3PhysChunkAgeingRolloverCallback, pVM);
}
else
RTAvlU32DoWithAll(&pVM->pgm.s.ChunkR3Map.pTree, true /*fFromLeft*/, pgmR3PhysChunkAgeingCallback, pVM);
}
/**
* The structure passed in the pvUser argument of pgmR3PhysChunkUnmapCandidateCallback().
*/
typedef struct PGMR3PHYSCHUNKUNMAPCB
{
/**
* Callback used to find the mapping that's been unused for
* the longest time.
*/
{
do
{
{
/*
* Check that it's not in any of the TLBs.
*/
{
break;
}
if (pChunk)
{
break;
}
if (pChunk)
{
return 1; /* done */
}
}
/* next with the same age - this version of the AVL API doesn't enumerate the list, so we have to do it. */
} while (pNode);
return 0;
}
/**
* Finds a good candidate for unmapping when the ring-3 mapping cache is full.
*
* The candidate will not be part of any TLBs, so no need to flush
* anything afterwards.
*
* @returns Chunk id.
* @param pVM The VM handle.
*/
{
/*
* Do tree ageing first?
*/
/*
* Enumerate the age tree starting with the left most node.
*/
if (RTAvllU32DoWithAll(&pVM->pgm.s.ChunkR3Map.pAgeTree, true /*fFromLeft*/, pgmR3PhysChunkUnmapCandidateCallback, pVM))
return INT32_MAX;
}
/**
* Maps the given chunk into the ring-3 mapping cache.
*
* This will call ring-0.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param idChunk The chunk in question.
* @param ppChunk Where to store the chunk tracking structure.
*
* @remarks Called from within the PGM critical section.
*/
{
int rc;
/*
* Allocate a new tracking structure first.
*/
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
PPGMCHUNKR3MAP pChunk = (PPGMCHUNKR3MAP)MMR3HeapAlloc(pVM, MM_TAG_PGM_CHUNK_MAPPING, sizeof(*pChunk));
#else
PPGMCHUNKR3MAP pChunk = (PPGMCHUNKR3MAP)MMR3UkHeapAlloc(pVM, MM_TAG_PGM_CHUNK_MAPPING, sizeof(*pChunk), NULL);
#endif
/*
* Request the ring-0 part to map the chunk in question and if
* necessary unmap another one to make space in the mapping cache.
*/
/** @todo This is wrong. Any thread in the VM process should be able to do this,
* there are depenenecies on this. What currently saves the day is that
* we don't unmap anything and that all non-zero memory will therefore
* be present when non-EMTs tries to access it. */
if (RT_SUCCESS(rc))
{
/*
* Update the tree.
*/
/* insert the new one. */
/* remove the unmapped one. */
{
PPGMCHUNKR3MAP pUnmappedChunk = (PPGMCHUNKR3MAP)RTAvlU32Remove(&pVM->pgm.s.ChunkR3Map.pTree, Req.idChunkUnmap);
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
#else
#endif
/* Chunk removed, so clear the page map TBL as well (might still be referenced). */
}
}
else
{
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
#else
#endif
}
return rc;
}
/**
* For VMMCALLRING3_PGM_MAP_CHUNK, considered internal.
*
* @returns see pgmR3PhysChunkMap.
* @param pVM The VM handle.
* @param idChunk The chunk to map.
*/
{
int rc;
return rc;
}
/**
* Invalidates the TLB for the ring-3 mapping cache.
*
* @param pVM The VM handle.
*/
{
{
}
/* The page map TLB references chunks, so invalidate that one too. */
}
/**
* Response to VMMCALLRING3_PGM_ALLOCATE_LARGE_PAGE to allocate a large (2MB) page
* for use with a nested paging PDE.
*
* @returns The following VBox status codes.
* @retval VINF_SUCCESS on success.
* @retval VINF_EM_NO_MEMORY if we're out of memory.
*
* @param pVM The VM handle.
* @param GCPhys GC physical start address of the 2 MB range
*/
{
if (RT_SUCCESS(rc))
{
void *pv;
/* Map the large page into our address space.
*
* Note: assuming that within the 2 MB range:
* - GCPhys + PAGE_SIZE = HCPhys + PAGE_SIZE (whole point of this exercise)
* - user space mapping is continuous as well
* - page id (GCPhys) + 1 = page id (GCPhys + PAGE_SIZE)
*/
if (RT_SUCCESS(rc))
{
/*
* Clear the pages.
*/
{
/*
* Do the PGMPAGE modifications.
*/
/* Somewhat dirty assumption that page ids are increasing. */
idPage++;
}
/* Flush all TLBs. */
}
}
return rc;
}
/**
* Response to VM_FF_PGM_NEED_HANDY_PAGES and VMMCALLRING3_PGM_ALLOCATE_HANDY_PAGES.
*
* This function will also work the VM_FF_PGM_NO_MEMORY force action flag, to
* signal and clear the out of memory condition. When contracted, this API is
* used to try clear the condition when the user wants to resume.
*
* @returns The following VBox status codes.
* @retval VINF_SUCCESS on success. FFs cleared.
* @retval VINF_EM_NO_MEMORY if we're out of memory. The FF is not cleared in
* this case and it gets accompanied by VM_FF_PGM_NO_MEMORY.
*
* @param pVM The VM handle.
*
* @remarks The VINF_EM_NO_MEMORY status is for the benefit of the FF processing
* in EM.cpp and shouldn't be propagated outside TRPM, HWACCM, EM and
* pgmPhysEnsureHandyPage. There is one exception to this in the \#PF
* handler.
*/
{
/*
* Allocate more pages, noting down the index of the first new page.
*/
AssertMsgReturn(iClear <= RT_ELEMENTS(pVM->pgm.s.aHandyPages), ("%d", iClear), VERR_INTERNAL_ERROR);
int rcAlloc = VINF_SUCCESS;
int rcSeed = VINF_SUCCESS;
while (rc == VERR_GMM_SEED_ME)
{
void *pvChunk;
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
{
/*
* Clear the pages.
*/
{
void *pv;
AssertLogRelMsgBreak(RT_SUCCESS(rc), ("idPage=%#x HCPhysGCPhys=%RHp rc=%Rrc", pPage->idPage, pPage->HCPhysGCPhys, rc));
iClear++;
Log3(("PGMR3PhysAllocateHandyPages: idPage=%#x HCPhys=%RGp\n", pPage->idPage, pPage->HCPhysGCPhys));
}
}
else
{
/*
* We should never get here unless there is a genuine shortage of
* memory (or some internal error). Flag the error so the VM can be
* suspended ASAP and the user informed. If we're totally out of
* handy pages we will return failure.
*/
/* Report the failure. */
LogRel(("PGM: Failed to procure handy pages; rc=%Rrc rcAlloc=%Rrc rcSeed=%Rrc cHandyPages=%#x\n"
" cAllPages=%#x cPrivatePages=%#x cSharedPages=%#x cZeroPages=%#x\n",
if ( rc != VERR_NO_MEMORY
&& rc != VERR_LOCK_FAILED)
{
{
LogRel(("PGM: aHandyPages[#%#04x] = {.HCPhysGCPhys=%RHp, .idPage=%#08x, .idSharedPage=%#08x}\n",
if (idPage != NIL_GMM_PAGEID)
{
pRam;
{
LogRel(("PGM: Used by %RGp %R[pgmpage] (%s)\n",
}
}
}
}
/* Set the FFs and adjust rc. */
if ( rc == VERR_NO_MEMORY
|| rc == VERR_LOCK_FAILED)
}
return rc;
}
/**
* Frees the specified RAM page and replaces it with the ZERO page.
*
* This is used by ballooning, remapping MMIO2 and RAM reset.
*
* @param pVM Pointer to the shared VM structure.
* @param pReq Pointer to the request.
* @param pPage Pointer to the page structure.
* @param GCPhys The guest physical address of the page, if applicable.
*
* @remarks The caller must own the PGM lock.
*/
static int pgmPhysFreePage(PVM pVM, PGMMFREEPAGESREQ pReq, uint32_t *pcPendingPages, PPGMPAGE pPage, RTGCPHYS GCPhys)
{
/*
* Assert sanity.
*/
{
return VMSetError(pVM, VERR_PGM_PHYS_NOT_RAM, RT_SRC_POS, "GCPhys=%RGp type=%d", GCPhys, PGM_PAGE_GET_TYPE(pPage));
}
return VINF_SUCCESS;
|| idPage > GMM_PAGEID_LAST
{
return VMSetError(pVM, VERR_PGM_PHYS_INVALID_PAGE_ID, RT_SRC_POS, "GCPhys=%RGp idPage=%#x", GCPhys, pPage);
}
/* update page count stats. */
if (PGM_PAGE_IS_SHARED(pPage))
else
/* Deal with write monitored pages. */
{
}
/*
* pPage = ZERO page.
*/
/* Flush physical page map TLB entry. */
/*
* Make sure it's not in the handy page array.
*/
{
{
break;
}
{
break;
}
}
/*
* Push it onto the page array.
*/
*pcPendingPages += 1;
return VINF_SUCCESS;
/*
* Flush the pages.
*/
if (RT_SUCCESS(rc))
{
*pcPendingPages = 0;
}
return rc;
}
/**
* Converts a GC physical address to a HC ring-3 pointer, with some
* additional checks.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success.
* @retval VINF_PGM_PHYS_TLB_CATCH_WRITE and *ppv set if the page has a write
* access handler of some kind.
* @retval VERR_PGM_PHYS_TLB_CATCH_ALL if the page has a handler catching all
* accesses or is odd in any way.
* @retval VERR_PGM_PHYS_TLB_UNASSIGNED if the page doesn't exist.
*
* @param pVM The VM handle.
* @param GCPhys The GC physical address to convert.
* @param fWritable Whether write access is required.
* @param ppv Where to store the pointer corresponding to GCPhys on
* success.
*/
{
if (RT_SUCCESS(rc))
{
if (!PGM_PAGE_HAS_ANY_HANDLERS(pPage))
rc = VINF_SUCCESS;
else
{
else if (PGM_PAGE_HAS_ACTIVE_HANDLERS(pPage))
{
/** @todo Handle TLB loads of virtual handlers so ./test.sh can be made to work
* in -norawr0 mode. */
if (fWritable)
}
else
{
/* Temporarily disabled physical handler(s), since the recompiler
doesn't get notified when it's reset we'll have to pretend it's
operating normally. */
else
}
}
if (RT_SUCCESS(rc))
{
int rc2;
/* Make sure what we return is writable. */
switch (PGM_PAGE_GET_STATE(pPage))
{
case PGM_PAGE_STATE_ALLOCATED:
break;
case PGM_PAGE_STATE_ZERO:
case PGM_PAGE_STATE_SHARED:
break;
}
/* Get a ring-3 mapping of the address. */
* pgmPhysPageLoadIntoTlb will repeat the lookup we've done here. */
Log6(("PGMR3PhysTlbGCPhys2Ptr: GCPhys=%RGp rc=%Rrc pPage=%R[pgmpage] *ppv=%p\n", GCPhys, rc, pPage, *ppv));
}
else
/* else: handler catching all access, no pointer returned. */
}
else
return rc;
}