PGMHandler.cpp revision 9abd3728add3fe375f787ef5091dc8b37481a98c
/* $Id$ */
/** @file
* PGM - Page Manager / Monitor, Access Handlers.
*/
/*
* Copyright (C) 2006-2007 innotek 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 (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
#include "PGMInternal.h"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Register a access handler for a physical range.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param enmType Handler type. Any of the PGMPHYSHANDLERTYPE_PHYSICAL* enums.
* @param GCPhys Start physical address.
* @param GCPhysLast Last physical address. (inclusive)
* @param pfnHandlerR3 The R3 handler.
* @param pvUserR3 User argument to the R3 handler.
* @param pszModR0 The R0 handler module. NULL means the default R0 module.
* @param pszHandlerR0 The R0 handler symbol name.
* @param pvUserR0 User argument to the R0 handler.
* @param pszModGC The GC handler module. NULL means the default GC module.
* @param pszHandlerGC The GC handler symbol name.
* @param pvUserGC User argument to the GC handler.
* This must be a GC pointer because it will be relocated!
* @param pszDesc Pointer to description string. This must not be freed.
*/
PGMR3DECL(int) PGMR3HandlerPhysicalRegister(PVM pVM, PGMPHYSHANDLERTYPE enmType, RTGCPHYS GCPhys, RTGCPHYS GCPhysLast,
{
LogFlow(("PGMR3HandlerPhysicalRegister: enmType=%d GCPhys=%VGv GCPhysLast=%VGv pfnHandlerR3=%VHv pvUserHC=%VHv pszModGC=%p:{%s} pszHandlerGC=%p:{%s} pvUser=%VGv pszDesc=%s\n",
enmType, GCPhys, GCPhysLast, pfnHandlerR3, pvUserR3, pszModGC, pszModGC, pszHandlerGC, pszHandlerGC, pvUserGC, pszDesc));
/*
* Validate input.
*/
if (!pszModGC)
if (!pszModR0)
/*
* Resolve the R0 handler.
*/
int rc = VINF_SUCCESS;
if (VBOX_SUCCESS(rc))
{
/*
* Resolve the GC handler.
*/
if (pszHandlerGC)
if (VBOX_SUCCESS(rc))
}
else
return rc;
}
/**
* Updates the physical page access handlers.
*
* @param pVM VM handle.
* @remark Only used when restoring a saved state.
*/
{
LogFlow(("pgmHandlerPhysicalUpdateAll:\n"));
/*
* Clear and set.
* (the right -> left on the setting pass is just bird speculating on cache hits)
*/
RTAvlroGCPhysDoWithAll(&pVM->pgm.s.CTXSUFF(pTrees)->PhysHandlers, true, pgmR3HandlerPhysicalOneClear, pVM);
RTAvlroGCPhysDoWithAll(&pVM->pgm.s.CTXSUFF(pTrees)->PhysHandlers, false, pgmR3HandlerPhysicalOneSet, pVM);
}
/**
* Clears all the page level flags for one physical handler range.
*
* @returns 0
* @param pNode Pointer to a PGMPHYSHANDLER.
* @param pvUser VM handle.
*/
{
for (;;)
{
if (RT_SUCCESS(rc))
else
if (--cPages == 0)
return 0;
}
}
/**
* Sets all the page level flags for one physical handler range.
*
* @returns 0
* @param pNode Pointer to a PGMPHYSHANDLER.
* @param pvUser VM handle.
*/
{
for (;;)
{
if (RT_SUCCESS(rc))
else
if (--cPages == 0)
return 0;
}
}
/**
* Register a access handler for a virtual range.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param enmType Handler type. Any of the PGMVIRTHANDLERTYPE_* enums.
* @param GCPtr Start address.
* @param GCPtrLast Last address (inclusive).
* @param pfnInvalidateHC The HC invalidate callback (can be 0)
* @param pfnHandlerHC The HC handler.
* @param pszHandlerGC The GC handler symbol name.
* @param pszModGC The GC handler module.
* @param pszDesc Pointer to description string. This must not be freed.
*/
/** @todo rename this function to PGMR3HandlerVirtualRegister */
PGMR3DECL(int) PGMR3HandlerVirtualRegister(PVM pVM, PGMVIRTHANDLERTYPE enmType, RTGCPTR GCPtr, RTGCPTR GCPtrLast,
const char *pszHandlerGC, const char *pszModGC,
const char *pszDesc)
{
LogFlow(("PGMR3HandlerVirtualRegisterEx: enmType=%d GCPtr=%VGv GCPtrLast=%VGv pszHandlerGC=%p:{%s} pszModGC=%p:{%s} pszDesc=%s\n",
/*
* Validate input.
*/
if (!pszModGC)
{
AssertMsgFailed(("pfnHandlerGC or/and pszModGC is missing\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Resolve the GC handler.
*/
if (VBOX_SUCCESS(rc))
return PGMHandlerVirtualRegisterEx(pVM, enmType, GCPtr, GCPtrLast, pfnInvalidateHC, pfnHandlerHC, pfnHandlerGC, pszDesc);
return rc;
}
/**
* Register an access handler for a virtual range.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param enmType Handler type. Any of the PGMVIRTHANDLERTYPE_* enums.
* @param GCPtr Start address.
* @param GCPtrLast Last address (inclusive).
* @param pfnInvalidateHC The HC invalidate callback (can be 0)
* @param pfnHandlerHC The HC handler.
* @param pfnHandlerGC The GC handler.
* @param pszDesc Pointer to description string. This must not be freed.
*/
/** @todo rename this to PGMR3HandlerVirtualRegisterEx. */
PGMDECL(int) PGMHandlerVirtualRegisterEx(PVM pVM, PGMVIRTHANDLERTYPE enmType, RTGCPTR GCPtr, RTGCPTR GCPtrLast,
{
Log(("PGMR3HandlerVirtualRegister: enmType=%d GCPtr=%RGv GCPtrLast=%RGv pfnHandlerGC=%RGv pszDesc=%s\n", enmType, GCPtr, GCPtrLast, pfnHandlerGC, pszDesc));
/*
* Validate input.
*/
switch (enmType)
{
case PGMVIRTHANDLERTYPE_ALL:
case PGMVIRTHANDLERTYPE_WRITE:
if (!pfnHandlerHC)
{
AssertMsgFailed(("No HC handler specified!!\n"));
return VERR_INVALID_PARAMETER;
}
break;
if (pfnHandlerHC)
{
AssertMsgFailed(("HC handler specified for hypervisor range!?!\n"));
return VERR_INVALID_PARAMETER;
}
break;
default:
return VERR_INVALID_PARAMETER;
}
{
return VERR_INVALID_PARAMETER;
}
if (!pfnHandlerGC)
{
AssertMsgFailed(("pfnHandlerGC is missing\n"));
return VERR_INVALID_PARAMETER;
}
/*
* Allocate and initialize a new entry.
*/
unsigned cPages = (RT_ALIGN((RTGCUINTPTR)GCPtrLast + 1, PAGE_SIZE) - ((RTGCUINTPTR)GCPtr & PAGE_BASE_GC_MASK)) >> PAGE_SHIFT;
int rc = MMHyperAlloc(pVM, RT_OFFSETOF(PGMVIRTHANDLER, aPhysToVirt[cPages]), 0, MM_TAG_PGM_HANDLERS, (void **)&pNew); /** @todo r=bird: incorrect member name PhysToVirt? */
if (VBOX_FAILURE(rc))
return rc;
/* Will be synced at next guest execution attempt. */
while (cPages-- > 0)
{
}
/*
* Try to insert it into the tree.
*
* The current implementation doesn't allow multiple handlers for
* the same range this makes everything much simpler and faster.
*/
{
PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrGetBestFit(&pVM->pgm.s.CTXSUFF(pTrees)->VirtHandlers, pNew->Core.Key, true);
pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrGetBestFit(&pVM->pgm.s.CTXSUFF(pTrees)->VirtHandlers, pNew->Core.Key, false);
{
/*
* The LDT sometimes conflicts with the IDT and LDT ranges while being
* updated on linux. So, we don't assert simply log it.
*/
Log(("PGMR3HandlerVirtualRegister: Conflict with existing range %RGv-%RGv (%s), req. %RGv-%RGv (%s)\n",
}
}
{
if (enmType != PGMVIRTHANDLERTYPE_HYPERVISOR)
{
}
#ifdef VBOX_WITH_STATISTICS
char szPath[256];
RTStrPrintf(szPath, sizeof(szPath), "/PGM/VirtHandler/Calls/%VGv-%VGv", pNew->GCPtr, pNew->GCPtrLast);
rc = STAMR3Register(pVM, &pNew->Stat, STAMTYPE_PROFILE, STAMVISIBILITY_USED, szPath, STAMUNIT_TICKS_PER_CALL, pszDesc);
#endif
return VINF_SUCCESS;
}
AssertFailed();
}
/**
* Modify the page invalidation callback handler for a registered virtual range
* (add more when needed)
*
* @returns VBox status code.
* @param pVM VM handle.
* @param GCPtr Start address.
* @param pfnInvalidateHC The HC invalidate callback (can be 0)
*/
PGMDECL(int) PGMHandlerVirtualChangeInvalidateCallback(PVM pVM, RTGCPTR GCPtr, PFNPGMHCVIRTINVALIDATE pfnInvalidateHC)
{
if (pCur)
{
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
/**
* Deregister an access handler for a virtual range.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param GCPtr Start address.
*/
{
/*
* Find the handler.
* We naturally assume GCPtr is a unique specification.
*/
PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrRemove(&pVM->pgm.s.CTXSUFF(pTrees)->VirtHandlers, GCPtr);
if (pCur)
{
/*
* Reset the flags and remove phys2virt nodes.
*/
/*
* Schedule CR3 sync (if required) and the memory.
*/
{
}
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
/**
* Arguments for pgmR3InfoHandlersPhysicalOne and pgmR3InfoHandlersVirtualOne.
*/
typedef struct PGMHANDLERINFOARG
{
/** The output helpers.*/
/** Set if statistics should be dumped. */
bool fStats;
/**
* Info callback for 'pgmhandlers'.
*
* @param pHlp The output helpers.
* @param pszArgs The arguments. phys or virt.
*/
{
/*
* Test input.
*/
if (!fPhysical)
{
}
/*
* Dump the handlers.
*/
if (fPhysical)
{
"Physical handlers: (PhysHandlers=%d (%#x))\n"
"From - To (incl) HandlerHC UserHC HandlerGC UserGC Type Description\n",
RTAvlroGCPhysDoWithAll(&pVM->pgm.s.pTreesHC->PhysHandlers, true, pgmR3InfoHandlersPhysicalOne, &Args);
}
if (fVirtual)
{
"Virtual handlers:\n"
"From - To (excl) HandlerHC HandlerGC Type Description\n");
RTAvlroGCPtrDoWithAll(&pVM->pgm.s.pTreesHC->VirtHandlers, true, pgmR3InfoHandlersVirtualOne, &Args);
}
}
/**
* Displays one physical handler range.
*
* @returns 0
* @param pNode Pointer to a PGMPHYSHANDLER.
* @param pvUser Pointer to command helper functions.
*/
{
const char *pszType;
{
default: pszType = "????"; break;
}
"%VGp - %VGp %VHv %VHv %VGv %VGv %s %s\n",
pCur->Core.Key, pCur->Core.KeyLast, pCur->pfnHandlerR3, pCur->pvUserR3, pCur->pfnHandlerGC, pCur->pvUserGC, pszType, pCur->pszDesc);
#ifdef VBOX_WITH_STATISTICS
#endif
return 0;
}
/**
* Displays one virtual handler range.
*
* @returns 0
* @param pNode Pointer to a PGMVIRTHANDLER.
* @param pvUser Pointer to command helper functions.
*/
{
const char *pszType;
{
default: pszType = "????"; break;
}
#ifdef VBOX_WITH_STATISTICS
#endif
return 0;
}