MMHyper.cpp revision 177784001589d6711c2e96ccc994ac07627b540c
/* $Id$ */
/** @file
* MM - Memory Manager - Hypervisor Memory Area.
*/
/*
* 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_MM_HYPER
#include "MMInternal.h"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(bool) mmR3HyperRelocateCallback(PVM pVM, RTGCPTR GCPtrOld, RTGCPTR GCPtrNew, PGMRELOCATECALL enmMode, void *pvUser);
static int mmR3HyperMap(PVM pVM, const size_t cb, const char *pszDesc, PRTGCPTR pGCPtr, PMMLOOKUPHYPER *ppLookup);
/**
* Initializes the hypvervisor related MM stuff without
* calling down to PGM.
*
* PGM is not initialized at this point, PGM relies on
* the heap to initialize.
*
* @returns VBox status.
*/
{
LogFlow(("mmR3HyperInit:\n"));
/*
* Decide Hypervisor mapping in the guest context
* And setup various hypervisor area and heap parameters.
*/
AssertRelease(RT_ALIGN_T(pVM->mm.s.pvHyperAreaGC, 1 << X86_PD_SHIFT, RTGCPTR) == pVM->mm.s.pvHyperAreaGC);
/** @todo @bugref{1865}, @bugref{3202}: Change the cbHyperHeap default
* precious kernel space on heap for the PATM. */
else if (VBOX_FAILURE(rc))
{
}
/*
* Allocate the hypervisor heap.
*
* (This must be done before we start adding memory to the
* hypervisor static area because lookup records are allocated from it.)
*/
if (VBOX_SUCCESS(rc))
{
pVM->mm.s.pHyperHeapR0 = (uintptr_t)pVM->mm.s.pHyperHeapR3; /** @todo #1865: map into ring-0 / whatever. */
/*
* Make a small head fence to fend of accidental sequential access.
*/
/*
* Map the VM structure into the hypervisor space.
*/
rc = MMR3HyperMapPages(pVM, pVM, pVM->pVMR0, RT_ALIGN_Z(sizeof(VM), PAGE_SIZE) >> PAGE_SHIFT, pVM->paVMPagesR3, "VM", &GCPtr);
if (VBOX_SUCCESS(rc))
{
/* Reserve a page for fencing. */
/*
* Map the heap into the hypervisor space.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Register info handlers.
*/
DBGFR3InfoRegisterInternal(pVM, "hma", "Show the layout of the Hypervisor Memory Area.", mmR3HyperInfoHma);
LogFlow(("mmR3HyperInit: returns VINF_SUCCESS\n"));
return VINF_SUCCESS;
}
/* Caller will do proper cleanup. */
}
}
return rc;
}
/**
* Finalizes the HMA mapping.
*
* This is called later during init, most (all) HMA allocations should be done
* by the time this function is called.
*
* @returns VBox status.
*/
{
LogFlow(("MMR3HyperInitFinalize:\n"));
/*
* Adjust and create the HMA mapping.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Do all the delayed mappings.
*/
PMMLOOKUPHYPER pLookup = (PMMLOOKUPHYPER)((uintptr_t)pVM->mm.s.pHyperHeapR3 + pVM->mm.s.offLookupHyper);
for (;;)
{
{
case MMLOOKUPHYPERTYPE_LOCKED:
break;
case MMLOOKUPHYPERTYPE_HCPHYS:
break;
case MMLOOKUPHYPERTYPE_GCPHYS:
{
{
if (VBOX_FAILURE(rc))
break;
if (VBOX_FAILURE(rc))
break;
}
break;
}
case MMLOOKUPHYPERTYPE_MMIO2:
{
{
rc = PGMR3PhysMMIO2GetHCPhys(pVM, pLookup->u.MMIO2.pDevIns, pLookup->u.MMIO2.iRegion, offCur, &HCPhys);
if (RT_FAILURE(rc))
break;
if (RT_FAILURE(rc))
break;
}
break;
}
/* do nothing here since these are either fences or managed by someone else using PGM. */
break;
default:
break;
}
if (VBOX_FAILURE(rc))
{
AssertMsgFailed(("rc=%Vrc cb=%d GCPtr=%VGv enmType=%d pszDesc=%s\n",
return rc;
}
/* next */
break;
}
LogFlow(("MMR3HyperInitFinalize: returns VINF_SUCCESS\n"));
return VINF_SUCCESS;
}
/**
* Callback function which will be called when PGM is trying to find
* a new location for the mapping.
*
* The callback is called in two modes, 1) the check mode and 2) the relocate mode.
* In 1) the callback should say if it objects to a suggested new location. If it
* accepts the new location, it is called again for doing it's relocation.
*
*
* @returns true if the location is ok.
* @returns false if another location should be found.
* @param pVM The VM handle.
* @param GCPtrOld The old virtual address.
* @param GCPtrNew The new virtual address.
* @param enmMode Used to indicate the callback mode.
* @param pvUser User argument. Ignored.
* @remark The return value is no a failure indicator, it's an acceptance
* indicator. Relocation can not fail!
*/
static DECLCALLBACK(bool) mmR3HyperRelocateCallback(PVM pVM, RTGCPTR GCPtrOld, RTGCPTR GCPtrNew, PGMRELOCATECALL enmMode, void *pvUser)
{
switch (enmMode)
{
/*
* Verify location - all locations are good for us.
*/
case PGMRELOCATECALL_SUGGEST:
return true;
/*
* Execute the relocation.
*/
case PGMRELOCATECALL_RELOCATE:
{
/*
* Accepted!
*/
AssertMsg(GCPtrOld == pVM->mm.s.pvHyperAreaGC, ("GCPtrOld=%VGv pVM->mm.s.pvHyperAreaGC=%VGv\n", GCPtrOld, pVM->mm.s.pvHyperAreaGC));
/*
* Relocate the VM structure and ourselves.
*/
/*
* Relocate the rest.
*/
return true;
}
default:
}
return false;
}
/**
* Maps contiguous HC physical memory into the hypervisor region in the GC.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param pvR3 Host context address of the memory. Must be page
* aligned!
* @param HCPhys Host context physical address of the memory to be
* mapped. Must be page aligned!
* @param cb Size of the memory. Will be rounded up to nearest page.
* @param pszDesc Description.
* @param pGCPtr Where to store the GC address.
*/
MMR3DECL(int) MMR3HyperMapHCPhys(PVM pVM, void *pvR3, RTHCPHYS HCPhys, size_t cb, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapHCPhys: pvR3=%p HCPhys=%RHp cb=%d pszDesc=%p:{%s} pGCPtr=%p\n", pvR3, HCPhys, (int)cb, pszDesc, pszDesc, pGCPtr));
/*
* Validate input.
*/
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Update the page table.
*/
if (VBOX_SUCCESS(rc))
}
return rc;
}
/**
* Maps contiguous GC physical memory into the hypervisor region in the GC.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param GCPhys Guest context physical address of the memory to be mapped. Must be page aligned!
* @param cb Size of the memory. Will be rounded up to nearest page.
* @param pszDesc Mapping description.
* @param pGCPtr Where to store the GC address.
*/
MMR3DECL(int) MMR3HyperMapGCPhys(PVM pVM, RTGCPHYS GCPhys, size_t cb, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapGCPhys: GCPhys=%VGp cb=%d pszDesc=%p:{%s} pGCPtr=%p\n", GCPhys, (int)cb, pszDesc, pszDesc, pGCPtr));
/*
* Validate input.
*/
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Update the page table.
*/
{
if (VBOX_FAILURE(rc))
{
break;
}
{
if (VBOX_FAILURE(rc))
{
break;
}
}
}
}
return rc;
}
/**
* Maps a portion of an MMIO2 region into the hypervisor region.
*
* Callers of this API must never deregister the MMIO2 region before the
* VM is powered off. If this becomes a requirement MMR3HyperUnmapMMIO2
* API will be needed to perform cleanups.
*
* @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. Will be rounded down to closest page boundrary.
* @param cb The number of bytes to map. Will be rounded up to the closest page boundrary.
* @param pszDesc Mapping description.
* @param pRCPtr Where to store the RC address.
*/
MMR3DECL(int) MMR3HyperMapMMIO2(PVM pVM, PPDMDEVINS pDevIns, uint32_t iRegion, RTGCPHYS off, RTGCPHYS cb,
{
LogFlow(("MMR3HyperMapMMIO2: pDevIns=%p iRegion=%#x off=%VGp cb=%VGp pszDesc=%p:{%s} pRCPtr=%p\n",
int rc;
/*
* Validate input.
*/
{
}
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Update the page table.
*/
{
{
if (VBOX_FAILURE(rc))
{
break;
}
}
}
if (VBOX_SUCCESS(rc))
{
}
}
return rc;
}
/**
* Locks and Maps HC virtual memory into the hypervisor region in the GC.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param pvR3 Host context address of the memory (may be not page
* aligned).
* @param cb Size of the memory. Will be rounded up to nearest page.
* @param fFree Set this if MM is responsible for freeing the memory
* using SUPPageFree.
* @param pszDesc Mapping description.
* @param pGCPtr Where to store the GC address corresponding to pvR3.
*/
MMR3DECL(int) MMR3HyperMapHCRam(PVM pVM, void *pvR3, size_t cb, bool fFree, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapHCRam: pvR3=%p cb=%d fFree=%d pszDesc=%p:{%s} pGCPtr=%p\n", pvR3, (int)cb, fFree, pszDesc, pszDesc, pGCPtr));
/*
* Validate input.
*/
if ( !pvR3
|| cb <= 0
|| !pszDesc
|| !*pszDesc)
{
AssertMsgFailed(("Invalid parameter\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Page align address and size.
*/
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Lock the heap memory and tell PGM about the locked pages.
*/
rc = mmR3LockMem(pVM, pvR3Page, cb, fFree ? MM_LOCKED_TYPE_HYPER : MM_LOCKED_TYPE_HYPER_NOFREE, &pLockedMem, false /* fSilentFailure */);
if (VBOX_SUCCESS(rc))
{
/* map the stuff into guest address space. */
if (VBOX_SUCCESS(rc))
{
/* done. */
return rc;
}
/* Don't care about failure clean, we're screwed if this fails anyway. */
}
}
return rc;
}
/**
* Maps locked R3 virtual memory into the hypervisor region in the GC.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param pvR3 The ring-3 address of the memory, must be page aligned.
* @param pvR0 The ring-0 address of the memory, must be page aligned. (optional)
* @param cPages The number of pages.
* @param paPages The page descriptors.
* @param pszDesc Mapping description.
* @param pGCPtr Where to store the GC address corresponding to pvR3.
*/
MMR3DECL(int) MMR3HyperMapPages(PVM pVM, void *pvR3, RTR0PTR pvR0, size_t cPages, PCSUPPAGE paPages, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapPages: pvR3=%p pvR0=%p cPages=%zu paPages=%p pszDesc=%p:{%s} pGCPtr=%p\n",
/*
* Validate input.
*/
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Create a locked memory record and tell PGM about this.
*/
PMMLOCKEDMEM pLockedMem = (PMMLOCKEDMEM)MMR3HeapAlloc(pVM, MM_TAG_MM, RT_OFFSETOF(MMLOCKEDMEM, aPhysPages[cPages]));
if (pLockedMem)
{
{
AssertReleaseReturn(paPages[i].Phys != 0 && paPages[i].Phys != NIL_RTHCPHYS && !(paPages[i].Phys & PAGE_OFFSET_MASK), VERR_INTERNAL_ERROR);
}
/* map the stuff into guest address space. */
if (VBOX_SUCCESS(rc))
{
/* done. */
return rc;
}
/* Don't care about failure clean, we're screwed if this fails anyway. */
}
}
return rc;
}
/**
* Reserves a hypervisor memory area.
* Most frequent usage is fence pages and dynamically mappings like the guest PD and PDPT.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param cb Size of the memory. Will be rounded up to nearest page.
* @param pszDesc Mapping description.
* @param pGCPtr Where to store the assigned GC address. Optional.
*/
{
LogFlow(("MMR3HyperMapHCRam: cb=%d pszDesc=%p:{%s} pGCPtr=%p\n", (int)cb, pszDesc, pszDesc, pGCPtr));
/*
* Validate input.
*/
if ( cb <= 0
|| !pszDesc
|| !*pszDesc)
{
AssertMsgFailed(("Invalid parameter\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Add the memory to the hypervisor area.
*/
if (VBOX_SUCCESS(rc))
{
if (pGCPtr)
return VINF_SUCCESS;
}
return rc;
}
/**
* Adds memory to the hypervisor memory arena.
*
* @return VBox status code.
* @param pVM The VM handle.
* @param cb Size of the memory. Will be rounded up to neares page.
* @param pszDesc The description of the memory.
* @param pGCPtr Where to store the GC address.
* @param ppLookup Where to store the pointer to the lookup record.
* @remark We assume the threading structure of VBox imposes natural
* serialization of most functions, this one included.
*/
static int mmR3HyperMap(PVM pVM, const size_t cb, const char *pszDesc, PRTGCPTR pGCPtr, PMMLOOKUPHYPER *ppLookup)
{
/*
* Validate input.
*/
if (pVM->mm.s.offHyperNextStatic + cbAligned >= pVM->mm.s.cbHyperArea) /* don't use the last page, it's a fence. */
{
AssertMsgFailed(("Out of static mapping space in the HMA! offHyperAreaGC=%x cbAligned=%x\n",
return VERR_NO_MEMORY;
}
/*
* Allocate lookup record.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Initialize it and insert it.
*/
/* Mapping. */
/* Return pointer. */
}
return rc;
}
/**
* Allocates a new heap.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param cb The size of the new heap.
* @param ppHeap Where to store the heap pointer on successful return.
*/
{
/*
* Allocate the hypervisor heap.
*/
void *pv;
int rc = SUPPageAlloc(cbAligned >> PAGE_SHIFT, &pv); /** @todo #1865: heap allocation must be changed for osx (only). */
if (VBOX_SUCCESS(rc))
{
/*
* Initialize the heap and first free chunk.
*/
//pHeap->pbHeapGC = 0; // set by mmR3HyperHeapMap()
//pHeap->offFreeHead = 0;
//pHeap->offFreeTail = 0;
//pHeap->HyperHeapStatTree = 0;
//pFree->core.offNext = 0;
//pFree->offNext = 0;
//pFree->offPrev = 0;
STAMR3Register(pVM, &pHeap->cbHeap, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, "/MM/HyperHeap/cbHeap", STAMUNIT_BYTES, "The heap size.");
STAMR3Register(pVM, &pHeap->cbFree, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, "/MM/HyperHeap/cbFree", STAMUNIT_BYTES, "The free space.");
return VINF_SUCCESS;
}
return rc;
}
/**
* Allocates a new heap.
*/
{
int rc = MMR3HyperMapHCRam(pVM, pHeap, pHeap->cbHeap + MMYPERHEAP_HDR_SIZE, true, "Heap", ppHeapGC);
if (VBOX_SUCCESS(rc))
{
/* Reserve a page for fencing. */
}
return rc;
}
#if 0
/**
* Destroys a heap.
*/
{
/* all this is dealt with when unlocking and freeing locked memory. */
}
#endif
/**
* Allocates memory in the Hypervisor (GC VMM) area which never will
* be freed and doesn't have any offset based relation to other heap blocks.
*
* The latter means that two blocks allocated by this API will not have the
* same relative position to each other in GC and HC. In short, never use
* this API for allocating nodes for an offset based AVL tree!
*
* The returned memory is of course zeroed.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param cb Number of bytes to allocate.
* @param uAlignment Required memory alignment in bytes.
* Values are 0,8,16,32 and PAGE_SIZE.
* 0 -> default alignment, i.e. 8 bytes.
* @param enmTag The statistics tag.
* @param ppv Where to store the address to the allocated
* memory.
* @remark This is assumed not to be used at times when serialization is required.
*/
MMDECL(int) MMR3HyperAllocOnceNoRel(PVM pVM, size_t cb, unsigned uAlignment, MMTAG enmTag, void **ppv)
{
/*
* Choose between allocating a new chunk of HMA memory
* and the heap. We will only do BIG allocations from HMA.
*/
&& ( uAlignment != PAGE_SIZE
{
if ( rc != VERR_MM_HYPER_NO_MEMORY
{
Log2(("MMR3HyperAllocOnceNoRel: cb=%#zx uAlignment=%#x returns %Rrc and *ppv=%p\n",
return rc;
}
}
/*
* Validate alignment.
*/
switch (uAlignment)
{
case 0:
case 8:
case 16:
case 32:
case PAGE_SIZE:
break;
default:
return VERR_INVALID_PARAMETER;
}
/*
* Allocate the pages and the HMA space.
*/
void *pvPages;
if (VBOX_SUCCESS(rc))
{
&GCPtr);
if (VBOX_SUCCESS(rc))
{
Log2(("MMR3HyperAllocOnceNoRel: cb=%#x uAlignment=%#x returns VINF_SUCCESS and *ppv=%p\n",
return rc;
}
/*
* HACK ALERT! Try allocate it off the heap so that we don't freak
*/
/** @todo make a proper fix for this so we will never end up in this kind of situation! */
Log(("MMR3HyperAllocOnceNoRel: MMR3HyperMapHCRam failed with rc=%Rrc, try MMHyperAlloc(,%#d,,) instead\n", rc, cb));
if (RT_SUCCESS(rc2))
{
Log2(("MMR3HyperAllocOnceNoRel: cb=%#x uAlignment=%#x returns %Rrc and *ppv=%p\n",
return rc;
}
}
else
if (rc == VERR_NO_MEMORY)
return rc;
}
/**
* Convert hypervisor HC virtual address to HC physical address.
*
* @returns HC physical address.
* @param pVM VM Handle
* @param pvR3 Host context virtual address.
*/
{
PMMLOOKUPHYPER pLookup = (PMMLOOKUPHYPER)((uint8_t *)pVM->mm.s.pHyperHeapR3 + pVM->mm.s.offLookupHyper);
for (;;)
{
{
case MMLOOKUPHYPERTYPE_LOCKED:
{
return (pLookup->u.Locked.pLockedMem->aPhysPages[off >> PAGE_SHIFT].Phys & X86_PTE_PAE_PG_MASK) | (off & PAGE_OFFSET_MASK);
break;
}
case MMLOOKUPHYPERTYPE_HCPHYS:
{
break;
}
case MMLOOKUPHYPERTYPE_GCPHYS:
case MMLOOKUPHYPERTYPE_MMIO2:
/* can (or don't want to) convert these kind of records. */
break;
default:
break;
}
/* next */
break;
}
return NIL_RTHCPHYS;
}
#if 0 /* unused, not implemented */
/**
* Convert hypervisor HC physical address to HC virtual address.
*
* @returns HC virtual address.
* @param pVM VM Handle
* @param HCPhys Host context physical address.
*/
{
void *pv;
if (VBOX_SUCCESS(rc))
return pv;
return NULL;
}
/**
* Convert hypervisor HC physical address to HC virtual address.
*
* @returns VBox status.
* @param pVM VM Handle
* @param HCPhys Host context physical address.
* @param ppv Where to store the HC virtual address.
*/
{
/*
* Linear search.
*/
/** @todo implement when actually used. */
return VERR_INVALID_POINTER;
}
#endif /* unused, not implemented */
/**
* Read hypervisor memory from GC virtual address.
*
* @returns VBox status.
* @param pVM VM handle.
* @param pvDst Destination address (HC of course).
* @param GCPtr GC virtual address.
* @param cb Number of bytes to read.
*
* @remarks For DBGF only.
*/
{
return VERR_INVALID_PARAMETER;
}
/**
* Info handler for 'hma', it dumps the list of lookup records for the hypervisor memory area.
*
* @param pVM The VM handle.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
{
PMMLOOKUPHYPER pLookup = (PMMLOOKUPHYPER)((uint8_t *)pVM->mm.s.pHyperHeapR3 + pVM->mm.s.offLookupHyper);
for (;;)
{
{
case MMLOOKUPHYPERTYPE_LOCKED:
sizeof(RTHCPTR) * 2,
: "??",
break;
case MMLOOKUPHYPERTYPE_HCPHYS:
break;
case MMLOOKUPHYPERTYPE_GCPHYS:
break;
case MMLOOKUPHYPERTYPE_MMIO2:
break;
break;
default:
break;
}
/* next */
break;
}
}