PGMPhys.cpp revision 17c6e5e8177d068d1bc6af875d1610718efcfdb4
/* $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
#include "PGMInternal.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.
*/
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
/*
* 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.
*
* @thread Any but EMTs.
*/
{
/*
* 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.
*/
{
/*
* It the page is in any way problematic, we have to
* do the work on the EMT. Anything that needs to be made
* writable or involves access handlers is problematic.
*/
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
/*
* 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;
}
#ifdef VBOX_WITH_NEW_PHYS_CODE
/**
* 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 1
if (PGM_PAGE_IS_MMIO(pPage))
#else
#endif
{
}
}
return rc;
}
#endif /* VBOX_WITH_NEW_PHYS_CODE */
/**
* 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)
{
#ifdef VBOX_WITH_NEW_PHYS_CODE
/*
* Query the Physical TLB entry for the page (may fail).
*/
if (RT_SUCCESS(rc))
{
#if 1
if (PGM_PAGE_IS_MMIO(pPage))
#else
#endif
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.
* This has to be done on an EMT.
*/
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
/*
* Now, just perform the locking and calculate the return address.
*/
#if 0 /** @todo implement locking properly */
{
}
#endif
}
}
return rc;
#else /* !VBOX_WITH_NEW_PHYS_CODE */
/*
* Fallback code.
*/
#endif /* !VBOX_WITH_NEW_PHYS_CODE */
}
/**
* 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)
{
#ifdef VBOX_WITH_NEW_PHYS_CODE
/*
* 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 0 /** @todo implement locking properly */
{
}
#endif
}
}
return rc;
#else /* !VBOX_WITH_NEW_PHYS_CODE */
/*
* Fallback code.
*/
#endif /* !VBOX_WITH_NEW_PHYS_CODE */
}
/**
* 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. */
{
}
}
/**
* 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;
/*
* Allocate RAM range.
*/
/*
* Initialize the range.
*/
#ifndef VBOX_WITH_NEW_PHYS_CODE
/* Allocate memory for chunk to HC ptr lookup array. */
rc = MMHyperAlloc(pVM, (cb >> PGM_DYNAMIC_CHUNK_SHIFT) * sizeof(void *), 16, MM_TAG_PGM, (void **)&pNew->paChunkR3Ptrs);
#endif
while (iPage-- > 0)
/* Update the page count stats. */
/*
* Insert the new RAM range.
*/
/*
* Notify REM.
*/
#ifdef VBOX_WITH_NEW_PHYS_CODE
#else
#endif
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.
*/
{
#ifdef VBOX_WITH_NEW_PHYS_CODE
/*
* We batch up pages before freeing them.
*/
uint32_t cPendingPages = 0;
#endif
/*
* Walk the ram ranges.
*/
{
#ifdef VBOX_WITH_NEW_PHYS_CODE
{
/* Replace all RAM pages by ZERO pages. */
while (iPage-- > 0)
{
switch (PGM_PAGE_GET_TYPE(pPage))
{
case PGMPAGETYPE_RAM:
if (!PGM_PAGE_IS_ZERO(pPage))
{
rc = pgmPhysFreePage(pVM, pReq, &cPendingPages, pPage, pRam->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT));
}
break;
case PGMPAGETYPE_MMIO2:
case PGMPAGETYPE_ROM_SHADOW: /* handled by pgmR3PhysRomReset. */
case PGMPAGETYPE_ROM:
case PGMPAGETYPE_MMIO:
break;
default:
AssertFailed();
}
} /* for each page */
}
else
#endif
{
/* Zero the memory. */
while (iPage-- > 0)
{
switch (PGM_PAGE_GET_TYPE(pPage))
{
#ifndef VBOX_WITH_NEW_PHYS_CODE
case PGMPAGETYPE_INVALID:
case PGMPAGETYPE_RAM:
if (pRam->aPages[iPage].HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2)) /** @todo PAGE FLAGS */
{
/* shadow ram is reloaded elsewhere. */
Log4(("PGMR3Reset: not clearing phys page %RGp due to flags %RHp\n", pRam->GCPhys + (iPage << PAGE_SHIFT), pRam->aPages[iPage].HCPhys & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO))); /** @todo PAGE FLAGS */
continue;
}
{
ASMMemZero32((char *)pRam->paChunkR3Ptrs[iChunk] + ((iPage << PAGE_SHIFT) & PGM_DYNAMIC_CHUNK_OFFSET_MASK), PAGE_SIZE);
}
else
break;
#else /* VBOX_WITH_NEW_PHYS_CODE */
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;
rc = pgmPhysPageMap(pVM, pPage, pRam->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT), &pMapIgnored, &pvPage);
break;
}
}
break;
#endif /* VBOX_WITH_NEW_PHYS_CODE */
case PGMPAGETYPE_MMIO2:
case PGMPAGETYPE_ROM_SHADOW:
case PGMPAGETYPE_ROM:
case PGMPAGETYPE_MMIO:
break;
default:
AssertFailed();
}
} /* for each page */
}
}
#ifdef VBOX_WITH_NEW_PHYS_CODE
/*
* Finish off any pages pending freeing.
*/
if (cPendingPages)
{
}
#endif
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)
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. */
#ifndef VBOX_WITH_NEW_PHYS_CODE
#endif
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))
{
{
/*if ( GCPhysLast >= pRam->GCPhys
&& GCPhys <= pRam->GCPhysLast) - later */
{
/*
* See if all the pages are dead MMIO pages.
*/
bool fAllMMIO = true;
while (cLeft-- > 0)
{
/*|| not-out-of-action later */)
{
fAllMMIO = false;
break;
}
pPage++;
}
/*
* Unlink it and free if it's all MMIO.
*/
if (fAllMMIO)
{
Log(("PGMR3PhysMMIODeregister: Freeing ad-hoc MMIO range for %RGp-%RGp %s\n",
}
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.
*/
/*
* Try reserve and allocate the backing memory first as this is what is
* most likely to fail.
*/
if (RT_FAILURE(rc))
return 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.fFlags = 0;
#ifndef VBOX_WITH_NEW_PHYS_CODE
#endif
while (iPage-- > 0)
{
}
/* update page count stats */
/*
* Link it into the list.
* Since there is no particular order, just push it.
*/
return VINF_SUCCESS;
}
}
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. */
/*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)
{
uint32_t cPendingPages = 0;
/* replace the pages, freeing all present RAM pages. */
while (cPagesLeft-- > 0)
{
pPageSrc++;
pPageDst++;
}
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.
*/
if (pCur->fOverlapping)
{
/* Restore the RAM pages we've replaced. */
while (cPagesLeft-- > 0)
{
pPageDst++;
}
}
else
{
}
pCur->fOverlapping = false;
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_FLAG_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_FLAG_SHADOWED | PGMPHYS_ROM_FLAG_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 isn't a RAM page (%d) - registering %RGp-%RGp (%s).\n",
pPage++;
}
}
/*
* Update the base memory reservation if necessary.
*/
if (fFlags & PGMPHYS_ROM_FLAG_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
{
{
}
}
/*
* !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_FLAG_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 */
/*
* 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)
{
{
/*
* Ignore.
*/
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...
*/
{
if (RT_FAILURE(rc))
{
return rc;
}
}
void *pvDstPage;
int rc2 = pgmPhysPageMap(pVM, &pRomPage->Shadow, GCPhys & X86_PTE_PG_MASK, &pMapIgnored, &pvDstPage);
if (RT_SUCCESS(rc2))
else
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.
*/
{
/* Count dirty shadow pages. */
while (iPage-- > 0)
cDirty++;
if (cDirty)
{
/* Free the dirty pages. */
{
iReqPage++;
}
/* setup the zero page. */
/* update the page count stats. */
}
}
else
{
/* clear all the pages. */
{
if (RT_FAILURE(rc))
break;
void *pvDstPage;
if (RT_FAILURE(rc))
break;
}
}
}
#ifdef VBOX_STRICT
/*
* Verify that the virgin page is unchanged if possible.
*/
if (pRom->pvOriginal)
{
{
void *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.
* @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.
*/
bool fFlushedPool = false;
{
/*
* Iterate the relevant pages and the ncessary make changes.
*/
bool fChanges = false;
iPage++)
{
{
fChanges = true;
/* flush the page pool first so we don't leave any usage references dangling. */
if (!fFlushedPool)
{
fFlushedPool = true;
}
/** @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)
{
}
/* Advance - cb isn't updated. */
}
return VINF_SUCCESS;
}
#ifndef VBOX_WITH_NEW_PHYS_CODE
/**
* Interface that the MMR3RamRegister(), MMR3RomRegister() and MMIO handler
* registration APIs calls to inform PGM about memory registrations.
*
* It registers the physical memory range with PGM. MM is responsible
* for the toplevel things - allocation and locking - while PGM is taking
* care of all the details and implements the physical address space virtualization.
*
* @returns VBox status.
* @param pVM The VM handle.
* @param pvRam HC virtual address of the RAM range. (page aligned)
* @param GCPhys GC physical address of the RAM range. (page aligned)
* @param cb Size of the RAM range. (page aligned)
* @param fFlags Flags, MM_RAM_*.
* @param paPages Pointer an array of physical page descriptors.
* @param pszDesc Description string.
*/
VMMR3DECL(int) PGMR3PhysRegister(PVM pVM, void *pvRam, RTGCPHYS GCPhys, size_t cb, unsigned fFlags, const SUPPAGE *paPages, const char *pszDesc)
{
/*
* Validate input.
* (Not so important because callers are only MMR3PhysRegister()
* and PGMR3HandlerPhysicalRegisterEx(), but anyway...)
*/
Assert((fFlags & (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_DYNAMIC_ALLOC)) || paPages);
/*Assert(!(fFlags & MM_RAM_FLAGS_RESERVED) || !paPages);*/
Assert((fFlags == (MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_MMIO)) || (fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC) || pvRam);
/*Assert(!(fFlags & MM_RAM_FLAGS_RESERVED) || !pvRam);*/
Assert(!(fFlags & ~(MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_DYNAMIC_ALLOC)));
if (GCPhysLast < GCPhys)
{
return VERR_INVALID_PARAMETER;
}
/*
* Find range location and check for conflicts.
*/
while (pCur)
{
{
AssertMsgFailed(("Conflict! This cannot happen!\n"));
return VERR_PGM_RAM_CONFLICT;
}
break;
/* next */
}
/*
* Allocate RAM range.
* Small ranges are allocated from the heap, big ones have separate mappings.
*/
int rc = VERR_NO_MEMORY;
{ /* large */
}
else
{ /* small */
}
if (RT_SUCCESS(rc))
{
/*
* Initialize the range.
*/
if (paPages)
{
while (iPage-- > 0)
{
}
}
else if (fFlags & MM_RAM_FLAGS_DYNAMIC_ALLOC)
{
/* Allocate memory for chunk to HC ptr lookup array. */
rc = MMHyperAlloc(pVM, (cb >> PGM_DYNAMIC_CHUNK_SHIFT) * sizeof(void *), 16, MM_TAG_PGM, (void **)&pNew->paChunkR3Ptrs);
/* Physical memory will be allocated on demand. */
while (iPage-- > 0)
{
}
}
else
{
while (iPage-- > 0)
{
PGM_PAGE_INIT(&pNew->aPages[iPage], HCPhysDummyPage, NIL_GMM_PAGEID, PGMPAGETYPE_MMIO, PGM_PAGE_STATE_ZERO);
}
}
/*
* Insert the new RAM range.
*/
if (pPrev)
{
}
else
{
}
}
return rc;
}
/**
* Register a chunk of a the physical memory range with PGM. MM is responsible
* for the toplevel things - allocation and locking - while PGM is taking
* care of all the details and implements the physical address space virtualization.
*
*
* @returns VBox status.
* @param pVM The VM handle.
* @param pvRam HC virtual address of the RAM range. (page aligned)
* @param GCPhys GC physical address of the RAM range. (page aligned)
* @param cb Size of the RAM range. (page aligned)
* @param fFlags Flags, MM_RAM_*.
* @param paPages Pointer an array of physical page descriptors.
* @param pszDesc Description string.
*/
VMMR3DECL(int) PGMR3PhysRegisterChunk(PVM pVM, void *pvRam, RTGCPHYS GCPhys, size_t cb, unsigned fFlags, const SUPPAGE *paPages, const char *pszDesc)
{
/*
* Validate input.
* (Not so important because callers are only MMR3PhysRegister()
* and PGMR3HandlerPhysicalRegisterEx(), but anyway...)
*/
Assert(!(fFlags & ~(MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2 | MM_RAM_FLAGS_DYNAMIC_ALLOC)));
if (GCPhysLast < GCPhys)
{
return VERR_INVALID_PARAMETER;
}
/*
* Find existing range location.
*/
while (pRam)
{
break;
}
if (paPages)
{
while (iPage-- > 0)
pRam->aPages[off + iPage].HCPhys = (paPages[iPage].Phys & X86_PTE_PAE_PG_MASK) | fFlags; /** @todo PAGE FLAGS */
}
/* Notify the recompiler. */
return VINF_SUCCESS;
}
/**
* Allocate missing physical pages for an existing guest RAM range.
*
* @returns VBox status.
* @param pVM The VM handle.
* @param GCPhys GC physical address of the RAM range. (page aligned)
*/
{
/*
* Walk range list.
*/
while (pRam)
{
{
bool fRangeExists = false;
/* Note: A request made from another thread may end up in EMT after somebody else has already allocated the range. */
fRangeExists = true;
if (fRangeExists)
return VINF_SUCCESS;
}
}
}
/**
* Allocate missing physical pages for an existing guest RAM range.
*
* @returns VBox status.
* @param pVM The VM handle.
* @param pRamRange RAM range
* @param GCPhys GC physical address of the RAM range. (page aligned)
*/
{
void *pvRam;
int rc;
/* We must execute this function in the EMT thread, otherwise we'll run into problems. */
{
AssertMsg(!PDMCritSectIsOwner(&pVM->pgm.s.CritSect), ("We own the PGM lock -> deadlock danger!!\n"));
rc = VMR3ReqCall(pVM, VMREQDEST_ANY, &pReq, RT_INDEFINITE_WAIT, (PFNRT)PGM3PhysGrowRange, 2, pVM, &GCPhysParam);
if (RT_SUCCESS(rc))
{
}
return rc;
}
/* Round down to chunk boundary */
for (;;)
{
if (RT_SUCCESS(rc))
{
rc = MMR3PhysRegisterEx(pVM, pvRam, GCPhys, PGM_DYNAMIC_CHUNK_SIZE, 0, MM_PHYS_TYPE_DYNALLOC_CHUNK, "Main Memory");
if (RT_SUCCESS(rc))
return rc;
}
if (enmVMState != VMSTATE_RUNNING)
{
LogRel(("PGM: Out of memory while trying to allocate a guest RAM chunk at %RGp (VMstate=%s)!\n", GCPhys, VMR3GetStateName(enmVMState)));
return rc;
}
LogRel(("pgmr3PhysGrowRange: out of memory. pause until the user resumes execution.\n"));
/* Pause first, then inform Main. */
VMSetRuntimeError(pVM, false, "HostMemoryLow", "Unable to allocate and lock memory. The virtual machine will be paused. Please close applications to free up memory or close the VM");
/* Wait for resume event; will only return in that case. If the VM is stopped, the EMT thread will be destroyed. */
/* Retry */
LogRel(("pgmr3PhysGrowRange: VM execution resumed -> retry.\n"));
}
}
/**
* Interface MMR3RomRegister() and MMR3PhysReserve calls to update the
* flags of existing RAM ranges.
*
* @returns VBox status.
* @param pVM The VM handle.
* @param GCPhys GC physical address of the RAM range. (page aligned)
* @param cb Size of the RAM range. (page aligned)
* @param fFlags The Or flags, MM_RAM_* \#defines.
* @param fMask The and mask for the flags.
*/
VMMR3DECL(int) PGMR3PhysSetFlags(PVM pVM, RTGCPHYS GCPhys, size_t cb, unsigned fFlags, unsigned fMask)
{
/*
* Validate input.
* (Not so important because caller is always MMR3RomRegister() and MMR3PhysReserve(), but anyway...)
*/
Assert(!(fFlags & ~(MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2)));
/*
* Lookup the range.
*/
if ( !pRam
{
return VERR_INVALID_PARAMETER;
}
/*
* Update the requested flags.
*/
RTHCPHYS fFullMask = ~(RTHCPHYS)(MM_RAM_FLAGS_RESERVED | MM_RAM_FLAGS_ROM | MM_RAM_FLAGS_MMIO | MM_RAM_FLAGS_MMIO2)
| fMask;
pRam->aPages[iPage].HCPhys = (pRam->aPages[iPage].HCPhys & fFullMask) | fFlags; /** @todo PAGE FLAGS */
return VINF_SUCCESS;
}
#endif /* !VBOX_WITH_NEW_PHYS_CODE */
/**
* Sets the Address Gate 20 state.
*
* @param pVM VM handle.
* @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.
*/
#if 0 /* for later when we've got a separate mapping method for ring-0. */
PPGMCHUNKR3MAP pChunk = (PPGMCHUNKR3MAP)MMR3HeapAlloc(pVM, MM_TAG_PGM_CHUNK_MAPPING, sizeof(*pChunk));
#else
#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.
*/
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);
#if 0 /* for later when we've got a separate mapping method for ring-0. */
#else
#endif
}
}
else
{
#if 0 /* for later when we've got a separate mapping method for ring-0. */
#else
#endif
}
return rc;
}
/**
* For VMMCALLHOST_PGM_MAP_CHUNK, considered internal.
*
* @returns see pgmR3PhysChunkMap.
* @param pVM The VM handle.
* @param idChunk The chunk to map.
*/
{
}
/**
* Invalidates the TLB for the ring-3 mapping cache.
*
* @param pVM The VM handle.
*/
{
{
}
}
/**
* Response to VM_FF_PGM_NEED_HANDY_PAGES and VMMCALLHOST_PGM_ALLOCATE_HANDY_PAGES.
*
* @returns The following VBox status codes.
* @retval VINF_SUCCESS on success. FF cleared.
* @retval VINF_EM_NO_MEMORY if we're out of memory. The FF is not cleared in this case.
*
* @param pVM The VM handle.
*/
{
/*
* 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);
while (rc == VERR_GMM_SEED_ME)
{
void *pvChunk;
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
if (RT_SUCCESS(rc))
}
/*
* Clear the pages.
*/
if (RT_SUCCESS(rc))
{
{
void *pv;
AssertLogRelMsgBreak(RT_SUCCESS(rc), ("idPage=%#x HCPhysGCPhys=%RHp rc=%Rrc", pPage->idPage, pPage->HCPhysGCPhys, rc));
iClear++;
}
}
else
{
LogRel(("PGM: Failed to procure handy pages, rc=%Rrc cHandyPages=%u\n",
//rc = VINF_EM_NO_MEMORY;
//VM_FF_SET(pVM, VM_FF_PGM_WE_ARE_SCREWED?);
}
/** @todo Do proper VERR_EM_NO_MEMORY reporting. */
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
/*
* pPage = ZERO page.
*/
/*
* Make sure it's not in the handy page array.
*/
{
{
break;
}
{
break;
}
i++;
}
/*
* 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))
{
#ifdef VBOX_WITH_NEW_PHYS_CODE
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
{
/* Temporariliy disabled phycial handler(s), since the recompiler
doesn't get notified when it's reset we'll have to pretend its
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 repeate 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
if (0)
/* nothing */;
else if (PGM_PAGE_HAS_ANY_HANDLERS(pPage))
{
else
{
/* Temporariliy disabled phycial handler(s), since the recompiler
doesn't get notified when it's reset we'll have to pretend its
operating normally. */
else
}
}
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
{
AssertMsg(PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM, ("GCPhys=%RGp type=%d\n", GCPhys, PGM_PAGE_GET_TYPE(pPage)));
}
{
AssertMsg(PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM || PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2, ("GCPhys=%RGp type=%d\n", GCPhys, PGM_PAGE_GET_TYPE(pPage)));
}
else
}
#endif /* !VBOX_WITH_NEW_PHYS_CODE */
}
else
return rc;
}