GMMR0.cpp revision 9342f8f598b5d058ef0a474be6680450bdf682e4
8e2911e5309f5dff976cc7ac17d832d4ee2cdca3vboxsync * GMM - Global Memory Manager.
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * Copyright (C) 2007-2012 Oracle Corporation
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * available from http://www.virtualbox.org. This file is free software;
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * you can redistribute it and/or modify it under the terms of the GNU
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * General Public License (GPL) as published by the Free Software
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync/** @page pg_gmm GMM - The Global Memory Manager
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * As the name indicates, this component is responsible for global memory
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * management. Currently only guest RAM is allocated from the GMM, but this
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * may change to include shadow page tables and other bits later.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Guest RAM is managed as individual pages, but allocated from the host OS
cba6719bd64ec749967bbe931230452664109857vboxsync * in chunks for reasons of portability / efficiency. To minimize the memory
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * footprint all tracking structure must be as small as possible without
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * unnecessary performance penalties.
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * The allocation chunks has fixed sized, the size defined at compile time
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * by the #GMM_CHUNK_SIZE \#define.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Each chunk is given an unique ID. Each page also has a unique ID. The
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * relation ship between the two IDs is:
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * GMM_CHUNK_SHIFT = log2(GMM_CHUNK_SIZE / PAGE_SIZE);
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * idPage = (idChunk << GMM_CHUNK_SHIFT) | iPage;
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Where iPage is the index of the page within the chunk. This ID scheme
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * permits for efficient chunk and page lookup, but it relies on the chunk size
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * to be set at compile time. The chunks are organized in an AVL tree with their
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync * IDs being the keys.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * The physical address of each page in an allocation chunk is maintained by
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * the #RTR0MEMOBJ and obtained using #RTR0MemObjGetPagePhysAddr. There is no
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * need to duplicate this information (it'll cost 8-bytes per page if we did).
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * So what do we need to track per page? Most importantly we need to know
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * which state the page is in:
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * - Private - Allocated for (eventually) backing one particular VM page.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * - Shared - Readonly page that is used by one or more VMs and treated
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * as COW by PGM.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * - Free - Not used by anyone.
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * For the page replacement operations (sharing, defragmenting and freeing)
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * to be somewhat efficient, private pages needs to be associated with a
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync * particular page in a particular VM.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Tracking the usage of shared pages is impractical and expensive, so we'll
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * settle for a reference counting system instead.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * Free pages will be chained on LIFOs
cba6719bd64ec749967bbe931230452664109857vboxsync * On 64-bit systems we will use a 64-bit bitfield per page, while on 32-bit
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * systems a 32-bit bitfield will have to suffice because of address space
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * limitations. The #GMMPAGE structure shows the details.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @section sec_gmm_alloc_strat Page Allocation Strategy
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * The strategy for allocating pages has to take fragmentation and shared
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * pages into account, or we may end up with with 2000 chunks with only
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * a few pages in each. Shared pages cannot easily be reallocated because
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync * of the inaccurate usage accounting (see above). Private pages can be
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * reallocated by a defragmentation thread in the same manner that sharing
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * The first approach is to manage the free pages in two sets depending on
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * whether they are mainly for the allocation of shared or private pages.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * In the initial implementation there will be almost no possibility for
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * mixing shared and private pages in the same chunk (only if we're really
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * stressed on memory), but when we implement forking of VMs and have to
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * deal with lots of COW pages it'll start getting kind of interesting.
b2b8d2ecd8d9bafa7d53919432c46e86fb377fa7vboxsync * The sets are lists of chunks with approximately the same number of
b2b8d2ecd8d9bafa7d53919432c46e86fb377fa7vboxsync * free pages. Say the chunk size is 1MB, meaning 256 pages, and a set
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * consists of 16 lists. So, the first list will contain the chunks with
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * 1-7 free pages, the second covers 8-15, and so on. The chunks will be
b2b8d2ecd8d9bafa7d53919432c46e86fb377fa7vboxsync * moved between the lists as pages are freed up or allocated.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @section sec_gmm_costs Costs
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * The per page cost in kernel space is 32-bit plus whatever RTR0MEMOBJ
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * entails. In addition there is the chunk cost of approximately
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * (sizeof(RT0MEMOBJ) + sizeof(CHUNK)) / 2^CHUNK_SHIFT bytes per page.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * On Windows the per page #RTR0MEMOBJ cost is 32-bit on 32-bit windows
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * and 64-bit on 64-bit windows (a PFN_NUMBER in the MDL). So, 64-bit per page.
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * The cost on Linux is identical, but here it's because of sizeof(struct page *).
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @section sec_gmm_legacy Legacy Mode for Non-Tier-1 Platforms
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * In legacy mode the page source is locked user pages and not
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * #RTR0MemObjAllocPhysNC, this means that a page can only be allocated
a39ea3668b7019c23a68936259545f9b71bce1aavboxsync * by the VM that locked it. We will make no attempt at implementing
da3503c04ce76e653401396fe2795a9bc2427a1dvboxsync * page sharing on these systems, just do enough to make it all work.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @subsection sub_gmm_locking Serializing
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * One simple fast mutex will be employed in the initial implementation, not
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * two as mentioned in @ref subsec_pgmPhys_Serializing.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * @see @ref subsec_pgmPhys_Serializing
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * @section sec_gmm_overcommit Memory Over-Commitment Management
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * The GVM will have to do the system wide memory over-commitment
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * management. My current ideas are:
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * - Per VM oc policy that indicates how much to initially commit
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync * to it and what to do in a out-of-memory situation.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * - Prevent overtaxing the host.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * There are some challenges here, the main ones are configurability and
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * security. Should we for instance permit anyone to request 100% memory
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * commitment? Who should be allowed to do runtime adjustments of the
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * config. And how to prevent these settings from being lost when the last
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * VM process exits? The solution is probably to have an optional root
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * daemon the will keep VMMR0.r0 in memory and enable the security measures.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @section sec_gmm_numa NUMA
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * NUMA considerations will be designed and implemented a bit later.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * The preliminary guesses is that we will have to try allocate memory as
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * close as possible to the CPUs the VM is executed on (EMT and additional CPU
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * threads). Which means it's mostly about allocation and sharing policies.
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync * Both the scheduler and allocator interface will to supply some NUMA info
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * and we'll need to have a way to calc access costs.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync/*******************************************************************************
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync* Header Files *
cba6719bd64ec749967bbe931230452664109857vboxsync*******************************************************************************/
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync/*******************************************************************************
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync* Structures and Typedefs *
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync*******************************************************************************/
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync/** Pointer to set of free chunks. */
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * The per-page tracking structure employed by the GMM.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * On 32-bit hosts we'll some trickery is necessary to compress all
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * the information into 32-bits. When the fSharedFree member is set,
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * the 30th bit decides whether it's a free page or not.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * Because of the different layout on 32-bit and 64-bit hosts, macros
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * are used to get and set some of the data.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsynctypedef union GMMPAGE
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync /** Unsigned integer view. */
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync /** The common view. */
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync /** The page state. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The view of a private page. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The guest page frame number. (Max addressable: 2 ^ 44 - 16) */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The GVM handle. (64K VMs) */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** Reserved. */
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync /** The page state. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The view of a shared page. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The host page frame number. (Max addressable: 2 ^ 44 - 16) */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The reference count (64K VMs). */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** Used for debug checksumming. */
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync /** The page state. */
dc1ef3adbbc24e348697c7f7d4b4df5cf4f64c32vboxsync /** The view of a free page. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The index of the next page in the free list. UINT16_MAX is NIL. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** Reserved. Checksum or something? */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** Reserved. Checksum or something? */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The page state. */
cba6719bd64ec749967bbe931230452664109857vboxsync#else /* 32-bit */
cba6719bd64ec749967bbe931230452664109857vboxsync /** Unsigned integer view. */
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync /** The common view. */
cba6719bd64ec749967bbe931230452664109857vboxsync /** The page state. */
struct GMMPAGEPRIVATE
} Private;
struct GMMPAGESHARED
} Shared;
struct GMMPAGEFREE
} Free;
} GMMPAGE;
#define GMM_PAGE_STATE_PRIVATE 0
typedef struct GMMCHUNKMAP
} GMMCHUNKMAP;
typedef struct GMMCHUNK
} GMMCHUNK;
typedef struct GMMCHUNKTLBE
} GMMCHUNKTLBE;
typedef struct GMMCHUNKTLB
} GMMCHUNKTLB;
typedef struct GMM
#ifdef VBOX_STRICT
bool fLegacyAllocationMode;
bool fBoundMemoryMode;
} GMM;
typedef struct GMMR0CHUNKMTXSTATE
typedef struct GMMR0SHMODPERVMDTORARGS
typedef struct GMMCHECKSHAREDMODULEINFO
typedef struct GMMFINDDUPPAGEINFO
bool fFoundDuplicate;
# define GMM_CHECK_SANITY_UPON_ENTERING(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
# define GMM_CHECK_SANITY_UPON_LEAVING(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
# define GMM_CHECK_SANITY_IN_LOOPS(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
#ifdef GMMR0_WITH_SANITY_CHECK
#ifdef VBOX_WITH_PAGE_SHARING
# ifdef VBOX_STRICT
if (!pGMM)
return VERR_NO_MEMORY;
unsigned iMtx;
# if defined(RT_OS_WINDOWS) || (defined(RT_OS_SOLARIS) && ARCH_BITS == 64) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
LogFlow(("GMMInit: pGMM=%p fLegacyAllocationMode=%RTbool fBoundMemoryMode=%RTbool\n", pGMM, pGMM->fLegacyAllocationMode, pGMM->fBoundMemoryMode));
return VINF_SUCCESS;
while (iMtx-- > 0)
return rc;
#ifdef VBOX_STRICT
return rc;
#ifdef VBOX_STRICT
return rc;
#ifdef VBOX_STRICT
#ifdef VBOX_STRICT
static int gmmR0ChunkMutexAcquire(PGMMR0CHUNKMTXSTATE pMtxState, PGMM pGMM, PGMMCHUNK pChunk, uint32_t fFlags)
return rc;
Assert((pMtxState->fFlags != GMMR0CHUNK_MTX_DROP_GIANT) == (pGMM->hMtxOwner == RTThreadNativeSelf()));
&& pChunk
return rc;
return GMM_CHUNK_NUMA_ID_UNKNOWN;
#ifdef VBOX_WITH_PAGE_SHARING
bool fRedoFromStart;
fRedoFromStart = false;
if (!iCountDown)
iCountDown--;
} while (fRedoFromStart);
SUPR0Printf("GMMR0CleanupVM: hGVM=%#x has %#x private pages that cannot be found!\n", pGVM->hSelf, pGVM->gmm.s.Stats.cPrivatePages);
fRedoFromStart = false;
while (pChunk)
if (fRedoFromStart)
if (--iCountDown == 0)
if (fRedoFromStart)
} while (fRedoFromStart);
SUPR0Printf("GMMR0CleanupVM: hGVM=%#x left %#x shared pages behind!\n", pGVM->hSelf, pGVM->gmm.s.Stats.cSharedPages);
case GMMOCPOLICY_NO_OC:
#ifndef VBOX_STRICT
unsigned cPrivate = 0;
unsigned cShared = 0;
unsigned cFree = 0;
while (iPage-- > 0)
cFree++;
cPrivate++;
cFree++;
cShared++;
SUPR0Printf("gmmR0CleanupVMScanChunk: Chunk %p/%#x has bogus stats - free=%d/%d private=%d/%d shared=%d/%d\n",
AssertMsgFailed(("%p/%#x: cFree=%#x - it should be 0 in bound mode!\n", pChunk, pChunk->Core.Key, pChunk->cFree));
for (unsigned i = 0; i < cMappings; i++)
cMappings--;
if (i < cMappings)
GMMR0DECL(int) GMMR0InitialReservation(PVM pVM, VMCPUID idCpu, uint64_t cBasePages, uint32_t cShadowPages, uint32_t cFixedPages,
LogFlow(("GMMR0InitialReservation: pVM=%p cBasePages=%#llx cShadowPages=%#x cFixedPages=%#x enmPolicy=%d enmPriority=%d\n",
return rc;
AssertReturn(enmPolicy > GMMOCPOLICY_INVALID && enmPolicy < GMMOCPOLICY_END, VERR_INVALID_PARAMETER);
AssertReturn(enmPriority > GMMPRIORITY_INVALID && enmPriority < GMMPRIORITY_END, VERR_INVALID_PARAMETER);
return rc;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return GMMR0InitialReservation(pVM, idCpu, pReq->cBasePages, pReq->cShadowPages, pReq->cFixedPages, pReq->enmPolicy, pReq->enmPriority);
GMMR0DECL(int) GMMR0UpdateReservation(PVM pVM, VMCPUID idCpu, uint64_t cBasePages, uint32_t cShadowPages, uint32_t cFixedPages)
return rc;
return rc;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
#ifdef GMMR0_WITH_SANITY_CHECK
cErrors++;
return cErrors;
return cErrors;
return pChunk;
return NULL;
return NIL_RTHCPHYS;
AssertMsg(iList < RT_SIZEOFMEMB(GMMCHUNKFREESET, apLists) / RT_SIZEOFMEMB(GMMCHUNKFREESET, apLists[0]),
return iList;
if (pPrev)
if (pNext)
return idChunk;
AssertMsgReturn(!ASMAtomicBitTestAndSet(&pGMM->bmChunkId[0], idChunk), ("%#x\n", idChunk), NIL_GMM_CHUNKID);
AssertMsgReturn(!ASMAtomicBitTestAndSet(&pGMM->bmChunkId[0], idChunk), ("%#x\n", idChunk), NIL_GMM_CHUNKID);
pPage->u = 0;
static uint32_t gmmR0AllocatePagesFromChunk(PGMMCHUNK pChunk, uint16_t const hGVM, uint32_t iPage, uint32_t cPages,
return iPage;
static int gmmR0RegisterChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, RTR0MEMOBJ MemObj, uint16_t hGVM, uint16_t fChunkFlags,
int rc;
if (pChunk)
LogFlow(("gmmR0RegisterChunk: pChunk=%p id=%#x cChunks=%d\n", pChunk, pChunk->Core.Key, pGMM->cChunks));
if (ppChunk)
return VINF_SUCCESS;
return rc;
return VINF_SUCCESS;
return rc;
while (iList-- > 0)
while (pChunk)
return iPage;
return iPage;
if (pChunk)
while (pChunk)
return iPage;
return iPage;
while (iList-- > 0)
while (pChunk)
return iPage;
return iPage;
return iPage;
while (pChunk)
return iPage;
return iPage;
static uint32_t gmmR0AllocatePagesInBoundMode(PGVM pGVM, uint32_t iPage, uint32_t cPages, PGMMPAGEDESC paPages)
while (pChunk)
return iPage;
return iPage;
static int gmmR0AllocatePagesNew(PGMM pGMM, PGVM pGVM, uint32_t cPages, PGMMPAGEDESC paPages, GMMACCOUNT enmAccount)
return VERR_GMM_HIT_GLOBAL_LIMIT;
switch (enmAccount)
case GMMACCOUNT_BASE:
if (RT_UNLIKELY( pGVM->gmm.s.Stats.Allocated.cBasePages + pGVM->gmm.s.Stats.cBalloonedPages + cPages
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
case GMMACCOUNT_SHADOW:
if (RT_UNLIKELY(pGVM->gmm.s.Stats.Allocated.cShadowPages + cPages > pGVM->gmm.s.Stats.Reserved.cShadowPages))
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
case GMMACCOUNT_FIXED:
if (RT_UNLIKELY(pGVM->gmm.s.Stats.Allocated.cFixedPages + cPages > pGVM->gmm.s.Stats.Reserved.cFixedPages))
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
return VERR_GMM_SEED_ME;
switch (enmAccount)
return VINF_SUCCESS;
switch (enmAccount)
while (iPage-- > 0)
return rc;
return VINF_SUCCESS;
GMMR0DECL(int) GMMR0AllocateHandyPages(PVM pVM, VMCPUID idCpu, uint32_t cPagesToUpdate, uint32_t cPagesToAlloc, PGMMPAGEDESC paPages)
return rc;
unsigned iPage = 0;
AssertMsgReturn(paPages[iPage].HCPhysGCPhys == NIL_RTHCPHYS, ("#%#x: %RHp\n", iPage, paPages[iPage].HCPhysGCPhys), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idPage), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idSharedPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idSharedPage), VERR_INVALID_PARAMETER);
Log(("GMMR0AllocateHandyPages: #%#x/%#x: Not private! %.*Rhxs (type %d)\n", iPage, paPages[iPage].idPage, sizeof(*pPage), pPage, pPage->Common.u2State));
Log(("GMMR0AllocateHandyPages: free shared page %x cRefs=%d\n", paPages[iPage].idSharedPage, pPage->Shared.cRefs));
Log(("GMMR0AllocateHandyPages: #%#x/%#x: Not found! (shared)\n", iPage, paPages[iPage].idSharedPage));
return rc;
GMMR0DECL(int) GMMR0AllocatePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMPAGEDESC paPages, GMMACCOUNT enmAccount)
LogFlow(("GMMR0AllocatePages: pVM=%p cPages=%#x paPages=%p enmAccount=%d\n", pVM, cPages, paPages, enmAccount));
return rc;
AssertMsgReturn(enmAccount > GMMACCOUNT_INVALID && enmAccount < GMMACCOUNT_END, ("%d\n", enmAccount), VERR_INVALID_PARAMETER);
AssertMsgReturn(cPages > 0 && cPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cPages), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idPage), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idSharedPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idSharedPage), VERR_INVALID_PARAMETER);
return rc;
GMMR0DECL(int) GMMR0AllocateLargePage(PVM pVM, VMCPUID idCpu, uint32_t cbPage, uint32_t *pIdPage, RTHCPHYS *pHCPhys)
return rc;
return VERR_NOT_SUPPORTED;
if (RT_UNLIKELY( pGVM->gmm.s.Stats.Allocated.cBasePages + pGVM->gmm.s.Stats.cBalloonedPages + cPages
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
return rc;
return rc;
return VERR_NOT_SUPPORTED;
Log(("GMMR0FreeLargePage: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Stats.Allocated.cBasePages, cPages));
return rc;
&& pGVM)
if (fRelaxedSem)
if (fRelaxedSem)
return fRelaxedSem;
static void gmmR0FreePageWorker(PGMM pGMM, PGVM pGVM, PGMMCHUNK pChunk, uint32_t idPage, PGMMPAGE pPage)
pPage, pPage - &pChunk->aPages[0], idPage, pPage->Common.u2State, pChunk->iFreeHead)); NOREF(idPage);
pPage->u = 0;
if ( !cFree
static int gmmR0FreePages(PGMM pGMM, PGVM pGVM, uint32_t cPages, PGMMFREEPAGEDESC paPages, GMMACCOUNT enmAccount)
switch (enmAccount)
case GMMACCOUNT_BASE:
Log(("gmmR0FreePages: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Stats.Allocated.cBasePages, cPages));
case GMMACCOUNT_SHADOW:
Log(("gmmR0FreePages: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Stats.Allocated.cShadowPages, cPages));
case GMMACCOUNT_FIXED:
Log(("gmmR0FreePages: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Stats.Allocated.cFixedPages, cPages));
switch (enmAccount)
return rc;
GMMR0DECL(int) GMMR0FreePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMFREEPAGEDESC paPages, GMMACCOUNT enmAccount)
LogFlow(("GMMR0FreePages: pVM=%p cPages=%#x paPages=%p enmAccount=%d\n", pVM, cPages, paPages, enmAccount));
return rc;
AssertMsgReturn(enmAccount > GMMACCOUNT_INVALID && enmAccount < GMMACCOUNT_END, ("%d\n", enmAccount), VERR_INVALID_PARAMETER);
AssertMsgReturn(cPages > 0 && cPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cPages), VERR_INVALID_PARAMETER);
return rc;
GMMR0DECL(int) GMMR0BalloonedPages(PVM pVM, VMCPUID idCpu, GMMBALLOONACTION enmAction, uint32_t cBalloonedPages)
AssertMsgReturn(cBalloonedPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cBalloonedPages), VERR_INVALID_PARAMETER);
return rc;
switch (enmAction)
case GMMBALLOONACTION_INFLATE:
if (RT_LIKELY(pGVM->gmm.s.Stats.Allocated.cBasePages + pGVM->gmm.s.Stats.cBalloonedPages + cBalloonedPages
/* Codepath never taken. Might be interesting in the future to request ballooned memory from guests in low memory conditions.. */
AssertFailed();
Log(("GMMR0BalloonedPages: +%#x - Global=%#llx / VM: Total=%#llx Req=%#llx Actual=%#llx (pending)\n",
case GMMBALLOONACTION_DEFLATE:
cBalloonedPages, pGMM->cBalloonedPages, pGVM->gmm.s.Stats.cBalloonedPages, pGVM->gmm.s.Stats.cReqDeflatePages));
Log(("GMMR0BalloonedPages: Total=%#llx cBalloonedPages=%#llx\n", pGVM->gmm.s.Stats.cBalloonedPages, cBalloonedPages));
case GMMBALLOONACTION_RESET:
return rc;
return VINF_SUCCESS;
return rc;
return rc;
cMappings--;
if (i < cMappings)
return rc;
Log(("gmmR0UnmapChunk: Chunk %#x is not mapped into pGVM=%p/%#x\n", pChunk->Core.Key, pGVM, pGVM->hSelf));
return VERR_GMM_CHUNK_NOT_MAPPED;
return rc;
return VINF_SUCCESS;
Log(("gmmR0UnmapChunk: Chunk %#x is not mapped into pGVM=%p/%#x (legacy)\n", pChunk->Core.Key, pGVM, pGVM->hSelf));
return VERR_GMM_CHUNK_NOT_MAPPED;
return VERR_GMM_CHUNK_NOT_FOUND;
return VINF_SUCCESS;
#ifdef VBOX_WITH_PAGE_SHARING
return VINF_SUCCESS;
return VERR_GMM_CHUNK_ALREADY_MAPPED;
int rc = RTR0MemObjMapUser(&hMapObj, pChunk->hMemObj, (RTR3PTR)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
return VERR_GMM_TOO_MANY_CHUNK_MAPPINGS;
return VERR_NO_MEMORY;
return rc;
return rc;
GMMR0DECL(int) GMMR0MapUnmapChunk(PVM pVM, uint32_t idChunkMap, uint32_t idChunkUnmap, PRTR3PTR ppvR3)
return rc;
return VERR_INVALID_PARAMETER;
return rc;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return rc;
return VERR_NOT_SUPPORTED;
rc = RTR0MemObjLockUser(&MemObj, pvR3, GMM_CHUNK_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
return rc;
#ifdef VBOX_WITH_PAGE_SHARING
# ifdef VBOX_STRICT
static PGMMSHAREDMODULE gmmR0ShModFindGlobal(PGMM pGMM, uint32_t uHash, uint32_t cbModule, VBOXOSFAMILY enmGuestOS,
for (PGMMSHAREDMODULE pGblMod = (PGMMSHAREDMODULE)RTAvllU32Get(&pGMM->pGlobalSharedModuleTree, uHash);
uint32_t i;
for (i = 0; i < cRegions; i++)
if (i == cRegions)
return pGblMod;
return NULL;
static int gmmR0ShModNewGlobal(PGMM pGMM, uint32_t uHash, uint32_t cbModule, VBOXOSFAMILY enmGuestOS,
Log(("gmmR0ShModNewGlobal: %s %s size %#x os %u rgn %u\n", pszModuleName, pszVersion, cbModule, cRegions));
return VERR_GMM_TOO_MANY_GLOBAL_MODULES;
PGMMSHAREDMODULE pGblMod = (PGMMSHAREDMODULE)RTMemAllocZ(RT_OFFSETOF(GMMSHAREDMODULE, aRegions[cRegions]));
if (!pGblMod)
return VERR_NO_MEMORY;
Log(("gmmR0ShModNewGlobal: rgn[%u]=%RGvLB%#x\n", i, paRegions[i].GCRegionAddr, paRegions[i].cbRegion));
return VINF_SUCCESS;
static int gmmR0ShModNewPerVM(PGVM pGVM, RTGCPTR GCBaseAddr, uint32_t cRegions, const VMMDEVSHAREDREGIONDESC *paRegions,
return VERR_GMM_TOO_MANY_PER_VM_MODULES;
pRecVM = (PGMMSHAREDMODULEPERVM)RTMemAllocZ(RT_OFFSETOF(GMMSHAREDMODULEPERVM, aRegionsGCPtrs[cRegions]));
if (!pRecVM)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
if (fRemove)
if (pGblMod)
GMMR0DECL(int) GMMR0RegisterSharedModule(PVM pVM, VMCPUID idCpu, VBOXOSFAMILY enmGuestOS, char *pszModuleName,
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
return VERR_GMM_TOO_MANY_REGIONS;
return VERR_GMM_BAD_SHARED_MODULE_SIZE;
return VERR_GMM_MODULE_NAME_TOO_LONG;
return VERR_GMM_MODULE_NAME_TOO_LONG;
Log(("GMMR0RegisterSharedModule %s %s base %RGv size %x hash %x\n", pszModuleName, pszVersion, GCPtrModBase, cbModule, uHash));
PGMMSHAREDMODULEPERVM pRecVM = (PGMMSHAREDMODULEPERVM)RTAvlGCPtrGet(&pGVM->gmm.s.pSharedModuleTree, GCPtrModBase);
if (fNewModule)
if (!pGblMod)
Log(("GMMR0RegisterSharedModule: new per vm module %s %s, gbl users %d\n", pszModuleName, pszVersion, pGblMod->cUsers));
Log(("GMMR0RegisterSharedModule: already registered %s %s, gbl users %d\n", pszModuleName, pszVersion, pGblMod->cUsers));
return rc;
return VERR_NOT_IMPLEMENTED;
GMMR0DECL(int) GMMR0RegisterSharedModuleReq(PVM pVM, VMCPUID idCpu, PGMMREGISTERSHAREDMODULEREQ pReq)
AssertMsgReturn(pReq->Hdr.cbReq >= sizeof(*pReq) && pReq->Hdr.cbReq == RT_UOFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[pReq->cRegions]), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
/* Pass back return code in the request packet to preserve informational codes. (VMMR3CallR0 chokes on them) */
return VINF_SUCCESS;
GMMR0DECL(int) GMMR0UnregisterSharedModule(PVM pVM, VMCPUID idCpu, char *pszModuleName, char *pszVersion,
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
return VERR_GMM_MODULE_NAME_TOO_LONG;
return VERR_GMM_MODULE_NAME_TOO_LONG;
Log(("GMMR0UnregisterSharedModule %s %s base=%RGv size %x\n", pszModuleName, pszVersion, GCPtrModBase, cbModule));
PGMMSHAREDMODULEPERVM pRecVM = (PGMMSHAREDMODULEPERVM)RTAvlGCPtrGet(&pGVM->gmm.s.pSharedModuleTree, GCPtrModBase);
if (pRecVM)
return rc;
NOREF(pVM); NOREF(idCpu); NOREF(pszModuleName); NOREF(pszVersion); NOREF(GCPtrModBase); NOREF(cbModule);
return VERR_NOT_IMPLEMENTED;
GMMR0DECL(int) GMMR0UnregisterSharedModuleReq(PVM pVM, VMCPUID idCpu, PGMMUNREGISTERSHAREDMODULEREQ pReq)
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return GMMR0UnregisterSharedModule(pVM, idCpu, pReq->szName, pReq->szVersion, pReq->GCBaseAddr, pReq->cbModule);
#ifdef VBOX_WITH_PAGE_SHARING
DECLINLINE(void) gmmR0ConvertToSharedPage(PGMM pGMM, PGVM pGVM, RTHCPHYS HCPhys, uint32_t idPage, PGMMPAGE pPage)
#ifdef VBOX_STRICT
AssertMsg(pPageDesc->GCPhys == (pPage->Private.pfn << 12), ("desc %RGp gmm %RGp\n", pPageDesc->HCPhys, (pPage->Private.pfn << 12)));
return VINF_SUCCESS;
GMMR0DECL(int) GMMR0SharedModuleCheckPage(PGVM pGVM, PGMMSHAREDMODULE pModule, uint32_t idxRegion, uint32_t idxPage,
int rc;
("idxRegion=%#x cRegions=%#x %s %s\n", idxRegion, pModule->cRegions, pModule->szName, pModule->szVersion),
("idxRegion=%#x cRegions=%#x %s %s\n", idxRegion, pModule->cRegions, pModule->szName, pModule->szVersion),
LogFlow(("GMMR0SharedModuleCheckRange %s base %RGv region %d idxPage %d\n", pModule->szName, pModule->Core.Key, idxRegion, idxPage));
return gmmR0SharedModuleCheckPageFirstTime(pGMM, pGVM, pModule, idxRegion, idxPage, pPageDesc, pGlobalRegion);
AssertMsgReturn(pPage, ("idPage=%#x (idxRegion=%#x idxPage=%#x) #2\n", pPageDesc->idPage, idxRegion, idxPage),
return gmmR0SharedModuleCheckPageFirstTime(pGMM, pGVM, pModule, idxRegion, idxPage, pPageDesc, pGlobalRegion);
Log(("Replace existing page guest host %RHp -> %RHp\n", pPageDesc->HCPhys, ((uint64_t)pPage->Shared.pfn) << PAGE_SHIFT));
AssertMsgReturn(pChunk, ("idPage=%#x (idxRegion=%#x idxPage=%#x) #4\n", pPageDesc->idPage, idxRegion, idxPage),
uint8_t *pbSharedPage = pbChunk + ((pGlobalRegion->paidPages[idxPage] & GMM_PAGEID_IDX_MASK) << PAGE_SHIFT);
#ifdef VBOX_STRICT
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
return rc;
return VERR_NOT_IMPLEMENTED;
#ifdef VBOX_WITH_PAGE_SHARING
int rc = PGMR0SharedModuleCheck(pArgs->pGVM->pVM, pArgs->pGVM, pArgs->idCpu, pGblMod, pRecVM->aRegionsGCPtrs);
return rc;
return VINF_SUCCESS;
#ifdef DEBUG_sandervl
return rc;
return VINF_SUCCESS;
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
# ifndef DEBUG_sandervl
rc = RTAvlGCPtrDoWithAll(&pGVM->gmm.s.pSharedModuleTree, true /* fFromLeft */, gmmR0CheckSharedModule, &Args);
# ifndef DEBUG_sandervl
return rc;
return VERR_NOT_IMPLEMENTED;
while (iPage-- > 0)
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return rc;
if (pChunk)
if (pPage)
AssertFailed();
AssertFailed();
AssertFailed();
return rc;
int rc;
if (pVM)
return rc;
return rc;
if (pGVM)
return rc;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return VINF_SUCCESS;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);