PGMPhys.cpp revision 2856f125faa24fa9173360af74346df4736ca53f
/* $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"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/*static - shut up warning */
DECLCALLBACK(int) pgmR3PhysRomWriteHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
/*
* 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"
/**
* 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)
/*
* 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.
*/
{
/*
* 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))
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;
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 */
}
}
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. */
while (iPage-- > 0)
/* 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;
while (iPage-- > 0)
{
}
/*
* 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.
*/
rc2 = MMR3AdjustFixedReservation(pVM, -(int32_t)(pCur->RamRange.cb >> PAGE_SHIFT), pCur->RamRange.pszDesc);
/* we're leaking hyper memory here if done at runtime. */
/*rc = MMHyperFree(pVM, pCur);
AssertRCReturn(rc, rc); - not safe, see the alloc call. */
/* 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)
{
/* replace the pages, freeing all present RAM pages. */
while (cPagesLeft-- > 0)
{
pPageSrc++;
pPageDst++;
}
}
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. */
#ifdef RT_STRICT
#endif
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
{
{
}
}
/*
* Register the write access handler for the range (PGMROMPROT_READ_ROM_WRITE_IGNORE).
*/
#if 0 /** @todo we actually need a ring-3 write handler here for shadowed ROMs, so hack REM! */
#else
#endif
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.
*/
{
}
/*
* Insert the ROM range, tell REM and return successfully.
*/
if (pRomPrev)
{
}
else
{
}
return VINF_SUCCESS;
}
/* bail out */
}
if (pRamNew)
}
}
/** @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 - shut up warning */
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;
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.
*/
if (1)///@todo !pVM->pgm.f.fRamPreAlloc)
{
/* Count dirty shadow pages. */
while (iPage-- > 0)
cDirty++;
if (cDirty)
{
/* Free the dirty pages. */
{
iReqPage++;
}
/* setup the zero page. */
}
}
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;
}
/**
* 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;
}
#ifndef VBOX_WITH_NEW_PHYS_CODE
/**
* 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"));
}
}
#endif /* !VBOX_WITH_NEW_PHYS_CODE */
/**
* 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;
}
/**
* 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.
*/
{
if (rc == VERR_GMM_SEED_ME)
{
void *pvChunk;
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
{
}
}
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 *pvPtr 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 pR3Ptr Where to store the R3 pointer on success.
*/
{
if (RT_SUCCESS(rc))
{
#if 0 /** @todo ifndef PGM_IGNORE_RAM_FLAGS_RESERVED */
#endif
{
#if 0 /** @todo looks like the system ROM is registered incorrectly, 0xfe000 claims to be a zero page. */
rc = VERR_PGM_PHYS_TLB_UNASSIGNED; /** @todo VBOX_WITH_NEW_PHYS_CODE: remap it to a writeable page. */
#else
if (0)
/* nothing */;
#endif
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
{
}
{
}
else
}
}
}
else
return rc;
}