PGMSharedPage.cpp revision 37bbf8d2728e3d798119c54a8fde51a02808ac47
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/* $Id$ */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/** @file
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * PGM - Page Manager and Monitor, Shared page handling
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/*
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * Copyright (C) 2006-2010 Oracle Corporation
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync *
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * available from http://www.virtualbox.org. This file is free software;
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * you can redistribute it and/or modify it under the terms of the GNU
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * General Public License (GPL) as published by the Free Software
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/*******************************************************************************
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync* Header Files *
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync*******************************************************************************/
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#define LOG_GROUP LOG_GROUP_PGM_SHARED
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/pgm.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/stam.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include "PGMInternal.h"
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include "PGMInline.h"
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/vm.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/sup.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/param.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/err.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <VBox/log.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <iprt/assert.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <iprt/asm.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <iprt/string.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#include <iprt/mem.h>
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#if defined(VBOX_STRICT) && HC_ARCH_BITS == 64
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/* Keep a copy of all registered shared modules for the .pgmcheckduppages debugger command. */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsyncstatic PGMMREGISTERSHAREDMODULEREQ pSharedModules[512] = {0};
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsyncstatic unsigned cSharedModules = 0;
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#endif
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync/**
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * Registers a new shared module for the VM
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync *
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @returns VBox status code.
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param pVM VM handle
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param enmGuestOS Guest OS type
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param pszModuleName Module name
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param pszVersion Module version
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param GCBaseAddr Module base address
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param cbModule Module size
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param cRegions Number of shared region descriptors
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync * @param pRegions Shared region(s)
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsyncVMMR3DECL(int) PGMR3SharedModuleRegister(PVM pVM, VBOXOSFAMILY enmGuestOS, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule,
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync unsigned cRegions, VMMDEVSHAREDREGIONDESC *pRegions)
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync{
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync#ifdef VBOX_WITH_PAGE_SHARING
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync PGMMREGISTERSHAREDMODULEREQ pReq;
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync Log(("PGMR3SharedModuleRegister family=%d name=%s version=%s base=%RGv size=%x cRegions=%d\n", enmGuestOS, pszModuleName, pszVersion, GCBaseAddr, cbModule, cRegions));
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync /* Sanity check. */
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync AssertReturn(cRegions < VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER);
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync pReq = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync AssertReturn(pReq, VERR_NO_MEMORY);
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync pReq->enmGuestOS = enmGuestOS;
4fedcd2da6f840dbadee84e579f146ad0f1fde22vboxsync pReq->GCBaseAddr = GCBaseAddr;
pReq->cbModule = cbModule;
pReq->cRegions = cRegions;
for (unsigned i = 0; i < cRegions; i++)
pReq->aRegions[i] = pRegions[i];
if ( RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName) != VINF_SUCCESS
|| RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion) != VINF_SUCCESS)
{
RTMemFree(pReq);
return VERR_BUFFER_OVERFLOW;
}
int rc = GMMR3RegisterSharedModule(pVM, pReq);
# if defined(VBOX_STRICT) && HC_ARCH_BITS == 64
if (rc == VINF_SUCCESS)
{
PGMMREGISTERSHAREDMODULEREQ *ppSharedModule = NULL;
if (pSharedModules[cSharedModules])
{
for (unsigned i = 0; i < cSharedModules; i++)
{
if (pSharedModules[cSharedModules] == NULL)
{
ppSharedModule = &pSharedModules[i];
break;
}
}
Assert(ppSharedModule);
}
else
ppSharedModule = &pSharedModules[cSharedModules];
*ppSharedModule = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
memcpy(*ppSharedModule, pReq, RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
cSharedModules++;
}
# endif
RTMemFree(pReq);
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
PGMMUNREGISTERSHAREDMODULEREQ pReq;
Log(("PGMR3SharedModuleUnregister name=%s version=%s base=%RGv size=%x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
pReq = (PGMMUNREGISTERSHAREDMODULEREQ)RTMemAllocZ(sizeof(*pReq));
AssertReturn(pReq, VERR_NO_MEMORY);
pReq->GCBaseAddr = GCBaseAddr;
pReq->cbModule = cbModule;
if ( RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName) != VINF_SUCCESS
|| RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion) != VINF_SUCCESS)
{
RTMemFree(pReq);
return VERR_BUFFER_OVERFLOW;
}
int rc = GMMR3UnregisterSharedModule(pVM, pReq);
RTMemFree(pReq);
# if defined(VBOX_STRICT) && HC_ARCH_BITS == 64
for (unsigned i = 0; i < cSharedModules; i++)
{
if ( !strcmp(pSharedModules[i]->szName, pszModuleName)
&& !strcmp(pSharedModules[i]->szVersion, pszVersion))
{
RTMemFree(pSharedModules[i]);
pSharedModules[i] = NULL;
cSharedModules--;
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)
{
VMCPUID idCpu = *(VMCPUID *)pvUser;
/* Execute on the VCPU that issued the original request to make sure we're in the right cr3 context. */
if (pVCpu->idCpu != idCpu)
{
Assert(pVM->cCpus > 1);
return VINF_SUCCESS;
}
/* Flush all pending handy page operations before changing any shared page assignments. */
int rc = PGMR3PhysAllocateHandyPages(pVM);
AssertRC(rc);
/* Lock it here as we can't deal with busy locks in this ring-0 path. */
pgmLock(pVM);
rc = GMMR3CheckSharedModules(pVM);
pgmUnlock(pVM);
AssertLogRelRC(rc);
return rc;
}
/**
* Shared module check helper (called on the way out).
*
* @param pVM The VM handle.
* @param VMCPUID VCPU id
*/
static DECLCALLBACK(void) pgmR3CheckSharedModulesHelper(PVM pVM, VMCPUID idCpu)
{
/* 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);
Assert(rc == VINF_SUCCESS);
}
#endif
/**
* Check all registered modules for changes.
*
* @returns VBox status code.
* @param pVM VM handle
*/
VMMR3DECL(int) PGMR3SharedModuleCheckAll(PVM pVM)
{
#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. */
RTGCPHYS GCPhys;
uint64_t fFlags;
pgmLock(pVM);
int rc = PGMGstGetPage(VMMGetCpu(pVM), GCPtrPage, &fFlags, &GCPhys);
switch (rc)
{
case VINF_SUCCESS:
{
PPGMPAGE pPage = pgmPhysGetPage(&pVM->pgm.s, GCPhys);
if (pPage)
{
*pfShared = PGM_PAGE_IS_SHARED(pPage);
*puPageFlags = fFlags;
}
else
rc = VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
break;
}
case VERR_PAGE_NOT_PRESENT:
case VERR_PAGE_TABLE_NOT_PRESENT:
case VERR_PAGE_MAP_LEVEL4_NOT_PRESENT:
case VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT:
*pfShared = false;
*puPageFlags = 0;
rc = VINF_SUCCESS;
break;
default:
break;
}
pgmUnlock(pVM);
return rc;
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
#if defined(VBOX_STRICT) && HC_ARCH_BITS == 64
/**
* 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;
pgmLock(pVM);
for (PPGMRAMRANGE pRam = pVM->pgm.s.pRamRangesR3; pRam; pRam = pRam->pNextR3)
{
PPGMPAGE pPage = &pRam->aPages[0];
RTGCPHYS GCPhys = pRam->GCPhys;
uint32_t cLeft = pRam->cb >> PAGE_SHIFT;
while (cLeft-- > 0)
{
if (PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM)
{
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:
case PGM_PAGE_STATE_WRITE_MONITORED:
{
const void *pvPage;
/* Check if the page was allocated, but completely zero. */
int rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, pPage, GCPhys, &pvPage);
if ( rc == VINF_SUCCESS
&& ASMMemIsZeroPage(pvPage))
{
cAllocZero++;
}
else
if (GMMR3IsDuplicatePage(pVM, PGM_PAGE_GET_PAGEID(pPage)))
cDuplicate++;
else
cUnique++;
break;
}
default:
AssertFailed();
break;
}
}
/* next */
pPage++;
GCPhys += PAGE_SIZE;
cPages++;
/* Give some feedback for every processed megabyte. */
if ((cPages & 0x7f) == 0)
pCmdHlp->pfnPrintf(pCmdHlp, NULL, ".");
}
}
pgmUnlock(pVM);
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\nNumber of zero pages %08x (%d MB)\n", cZero, cZero / 256);
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 shared pages %08x (%d MB)\n", cShared, cShared / 256);
pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of unique pages %08x (%d MB)\n", cUnique, cUnique / 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;
pgmLock(pVM);
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", j, pSharedModules[i]->aRegions[j].GCRegionAddr, pSharedModules[i]->aRegions[j].cbRegion);
}
i++;
}
}
while (i < cSharedModules);
pgmUnlock(pVM);
return VINF_SUCCESS;
}
#endif /* VBOX_STRICT && HC_ARCH_BITS == 64*/