MMHyper.cpp revision 37eb780874007e6c73f493edcfd7a1e498a6a2ef
/* $Id$ */
/** @file
* MM - Memory Monitor(/Manager) - Hypervisor Memory Area.
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* 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 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* 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);
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))
{
/*
* 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", &pVM->pVMGC);
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.pHyperHeapHC + 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;
}
/* 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=%#x pVM->mm.s.pvHyperAreaGC=%#x\n", GCPtrOld, pVM->mm.s.pvHyperAreaGC));
/* relocate our selves and the VM structure. */
/* 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 pvHC 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 *pvHC, RTHCPHYS HCPhys, size_t cb, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapHCPhys: pvHc=%p HCPhys=%VHp cb=%d pszDesc=%p:{%s} pGCPtr=%p\n", pvHC, 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;
}
/**
* Locks and Maps HC virtual memory into the hypervisor region in the GC.
*
* @return VBox status code.
*
* @param pVM VM handle.
* @param pvHC 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 pvHC.
*/
MMR3DECL(int) MMR3HyperMapHCRam(PVM pVM, void *pvHC, size_t cb, bool fFree, const char *pszDesc, PRTGCPTR pGCPtr)
{
LogFlow(("MMR3HyperMapHCRam: pvHc=%p cb=%d fFree=%d pszDesc=%p:{%s} pGCPtr=%p\n", pvHC, (int)cb, fFree, pszDesc, pszDesc, pGCPtr));
/*
* Validate input.
*/
if ( !pvHC
|| 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, pvHCPage, 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 pvHC.
*/
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 PDPTR.
*
* @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;
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)
{
AssertMsg(cb <= _4M, ("Allocating more than 4MB!? (cb=%#x) HMA limit might need adjusting if you allocate more.\n", cb));
/*
* 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=%#x 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))
{
if (VBOX_SUCCESS(rc))
{
Log2(("MMR3HyperAllocOnceNoRel: cb=%#x uAlignment=%#x returns VINF_SUCCESS and *ppv=%p\n",
return rc;
}
}
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 pvHC Host context physical address.
*/
{
PMMLOOKUPHYPER pLookup = (PMMLOOKUPHYPER)((char*)pVM->mm.s.pHyperHeapHC + 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:
/* can 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.
*/
{
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)((char*)pVM->mm.s.pHyperHeapHC + pVM->mm.s.offLookupHyper);
for (;;)
{
{
case MMLOOKUPHYPERTYPE_LOCKED:
sizeof(RTHCPTR) * 2,
: "??",
break;
case MMLOOKUPHYPERTYPE_HCPHYS:
break;
case MMLOOKUPHYPERTYPE_GCPHYS:
break;
break;
default:
break;
}
/* next */
break;
}
}