PGMSharedPage.cpp revision 652d85a9390f54d4c6eca560340bf67ac1f85c9d
/* $Id$ */
/** @file
* PGM - Page Manager and Monitor, Shared page handling
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PGM_SHARED
#include "PGMInternal.h"
#include "PGMInline.h"
/* Keep a copy of all registered shared modules for the .pgmcheckduppages debugger command. */
static unsigned cSharedModules = 0;
#endif
/**
* Registers a new shared module for the VM
*
* @returns VBox status code.
* @param pVM VM handle
* @param enmGuestOS Guest OS type
* @param pszModuleName Module name
* @param pszVersion Module version
* @param GCBaseAddr Module base address
* @param cbModule Module size
* @param cRegions Number of shared region descriptors
* @param pRegions Shared region(s)
*/
VMMR3DECL(int) PGMR3SharedModuleRegister(PVM pVM, VBOXOSFAMILY enmGuestOS, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule,
{
#ifdef VBOX_WITH_PAGE_SHARING
Log(("PGMR3SharedModuleRegister family=%d name=%s version=%s base=%RGv size=%x cRegions=%d\n", enmGuestOS, pszModuleName, pszVersion, GCBaseAddr, cbModule, cRegions));
/* Sanity check. */
pReq = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
for (unsigned i = 0; i < cRegions; i++)
{
return VERR_BUFFER_OVERFLOW;
}
if (rc == VINF_SUCCESS)
{
if (pSharedModules[cSharedModules])
{
for (unsigned i = 0; i < cSharedModules; i++)
{
{
ppSharedModule = &pSharedModules[i];
break;
}
}
}
else
*ppSharedModule = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
}
# endif
Assert(rc == VINF_SUCCESS || rc == VINF_PGM_SHARED_MODULE_COLLISION || rc == VINF_PGM_SHARED_MODULE_ALREADY_REGISTERED);
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
/**
* Unregisters a shared module for the VM
*
* @returns VBox status code.
* @param pVM VM handle
* @param pszModuleName Module name
* @param pszVersion Module version
* @param GCBaseAddr Module base address
* @param cbModule Module size
*/
VMMR3DECL(int) PGMR3SharedModuleUnregister(PVM pVM, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule)
{
#ifdef VBOX_WITH_PAGE_SHARING
Log(("PGMR3SharedModuleUnregister name=%s version=%s base=%RGv size=%x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
{
return VERR_BUFFER_OVERFLOW;
}
for (unsigned i = 0; i < cSharedModules; i++)
{
{
RTMemFree(pSharedModules[i]);
pSharedModules[i] = NULL;
break;
}
}
# endif
return rc;
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
#ifdef VBOX_WITH_PAGE_SHARING
/**
* Rendezvous callback that will be called once.
*
* @returns VBox strict status code.
* @param pVM VM handle.
* @param pVCpu The VMCPU handle for the calling EMT.
* @param pvUser Not used;
*/
static DECLCALLBACK(VBOXSTRICTRC) pgmR3SharedModuleRegRendezvous(PVM pVM, PVMCPU pVCpu, void *pvUser)
{
/* Execute on the VCPU that issued the original request to make sure we're in the right cr3 context. */
{
return VINF_SUCCESS;
}
/* Flush all pending handy page operations before changing any shared page assignments. */
/* Lock it here as we can't deal with busy locks in this ring-0 path. */
return rc;
}
/**
* Shared module check helper (called on the way out).
*
* @param pVM The VM handle.
* @param VMCPUID VCPU id
*/
{
/* We must stall other VCPUs as we'd otherwise have to send IPI flush commands for every single change we make. */
int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONE_BY_ONE, pgmR3SharedModuleRegRendezvous, &idCpu);
}
#endif
/**
* Check all registered modules for changes.
*
* @returns VBox status code.
* @param pVM VM handle
*/
{
#ifdef VBOX_WITH_PAGE_SHARING
/* Queue the actual registration as we are under the IOM lock right now. Perform this operation on the way out. */
return VMR3ReqCallNoWait(pVM, VMCPUID_ANY_QUEUE, (PFNRT)pgmR3CheckSharedModulesHelper, 2, pVM, VMMGetCpuId(pVM));
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
/**
* Query the state of a page in a shared module
*
* @returns VBox status code.
* @param pVM VM handle
* @param GCPtrPage Page address
* @param pfShared Shared status (out)
* @param puPageFlags Page flags (out)
*/
VMMR3DECL(int) PGMR3SharedModuleGetPageState(PVM pVM, RTGCPTR GCPtrPage, bool *pfShared, uint64_t *puPageFlags)
{
#if defined(VBOX_WITH_PAGE_SHARING) && defined(DEBUG)
/* Debug only API for the page fusion testcase. */
switch (rc)
{
case VINF_SUCCESS:
{
if (pPage)
{
*puPageFlags = fFlags;
}
else
break;
}
case VERR_PAGE_NOT_PRESENT:
*pfShared = false;
*puPageFlags = 0;
rc = VINF_SUCCESS;
break;
default:
break;
}
return rc;
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
/**
* The '.pgmcheckduppages' command.
*
* @returns VBox status.
* @param pCmd Pointer to the command descriptor (as registered).
* @param pCmdHlp Pointer to command helper functions.
* @param pVM Pointer to the current VM (if any).
* @param paArgs Pointer to (readonly) array of arguments.
* @param cArgs Number of arguments in the array.
*/
DECLCALLBACK(int) pgmR3CmdCheckDuplicatePages(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
{
unsigned cBallooned = 0;
unsigned cShared = 0;
unsigned cZero = 0;
unsigned cUnique = 0;
unsigned cDuplicate = 0;
unsigned cAllocZero = 0;
unsigned cPages = 0;
{
while (cLeft-- > 0)
{
{
switch (PGM_PAGE_GET_STATE(pPage))
{
case PGM_PAGE_STATE_ZERO:
cZero++;
break;
case PGM_PAGE_STATE_BALLOONED:
cBallooned++;
break;
case PGM_PAGE_STATE_SHARED:
cShared++;
break;
case PGM_PAGE_STATE_ALLOCATED:
{
const void *pvPage;
/* Check if the page was allocated, but completely zero. */
if ( rc == VINF_SUCCESS
&& ASMMemIsZeroPage(pvPage))
{
cAllocZero++;
}
else
cDuplicate++;
else
cUnique++;
break;
}
default:
AssertFailed();
break;
}
}
/* next */
pPage++;
cPages++;
/* Give some feedback for every processed megabyte. */
if ((cPages & 0x7f) == 0)
}
}
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of alloczero pages %08x (%d MB)\n", cAllocZero, cAllocZero / 256);
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of ballooned pages %08x (%d MB)\n", cBallooned, cBallooned / 256);
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of duplicate pages %08x (%d MB)\n", cDuplicate, cDuplicate / 256);
return VINF_SUCCESS;
}
/**
* The '.pgmsharedmodules' command.
*
* @returns VBox status.
* @param pCmd Pointer to the command descriptor (as registered).
* @param pCmdHlp Pointer to command helper functions.
* @param pVM Pointer to the current VM (if any).
* @param paArgs Pointer to (readonly) array of arguments.
* @param cArgs Number of arguments in the array.
*/
DECLCALLBACK(int) pgmR3CmdShowSharedModules(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
{
unsigned i = 0;
do
{
if (pSharedModules[i])
{
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Shared module %s (%s):\n", pSharedModules[i]->szName, pSharedModules[i]->szVersion);
for (unsigned j = 0; j < pSharedModules[i]->cRegions; j++)
{
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "--- Region %d: base %RGv size %x\n", pSharedModules[i]->aRegions[j].GCRegionAddr, pSharedModules[i]->aRegions[j].cbRegion);
}
i++;
}
}
while (i < cSharedModules);
return VINF_SUCCESS;
}
#endif /* VBOX_STRICT && HC_ARCH_BITS == 64*/