PATMSSM.cpp revision bba5b0889e561826d405efd16eaa8c38694ac291
/* $Id$ */
/** @file
* PATMSSM - Dynamic Guest OS Patching Manager; Save and load state
*
* NOTE: CSAM assumes patch memory is never reused!!
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PATM
#include "PATMInternal.h"
#include "PATMPatch.h"
#include "PATMA.h"
#include <VBox/disopcode.h>
static void patmCorrectFixup(PVM pVM, unsigned ulSSMVersion, PATM &patmInfo, PPATCHINFO pPatch, PRELOCREC pRec, int32_t offset, RTRCPTR *pFixup);
#ifdef VBOX_STRICT
/**
* Callback function for RTAvlPVDoWithAll
*
* Counts the number of patches in the tree
*
* @returns VBox status code.
* @param pNode Current node
* @param pcPatches Pointer to patch counter (uint32_t)
*/
{
return VINF_SUCCESS;
}
/**
* Callback function for RTAvlU32DoWithAll
*
* Counts the number of patches in the tree
*
* @returns VBox status code.
* @param pNode Current node
* @param pcPatches Pointer to patch counter (uint32_t)
*/
{
return VINF_SUCCESS;
}
#endif /* VBOX_STRICT */
/**
* Callback function for RTAvloU32DoWithAll
*
* Counts the number of patches in the tree
*
* @returns VBox status code.
* @param pNode Current node
* @param pcPatches Pointer to patch counter
*/
{
return VINF_SUCCESS;
}
/**
* Callback function for RTAvlU32DoWithAll
*
* Saves all patch to guest lookup records.
*
* @returns VBox status code.
* @param pNode Current node
* @param pVM1 VM Handle
*/
{
/* Save the lookup record. */
return VINF_SUCCESS;
}
/**
* Callback function for RTAvlPVDoWithAll
*
* Saves all patch to guest lookup records.
*
* @returns VBox status code.
* @param pNode Current node
* @param pVM1 VM Handle
*/
{
/* Save the lookup record. */
return VINF_SUCCESS;
}
/**
* Callback function for RTAvloU32DoWithAll
*
* Saves the state of the patch that's being enumerated
*
* @returns VBox status code.
* @param pNode Current node
* @param pVM1 VM Handle
*/
{
int rc;
/*
* Reset HC pointers that need to be recalculated when loading the state
*/
AssertMsg(patch.patch.uState == PATCH_REFUSED || (patch.patch.pPatchBlockOffset || (patch.patch.flags & (PATMFL_SYSENTER_XP|PATMFL_INT3_REPLACEMENT))),
("State = %x pPrivInstrHC=%08x pPatchBlockHC=%08x flags=%x\n", patch.patch.uState, patch.patch.pPrivInstrHC, PATCHCODE_PTR_HC(&patch.patch), patch.patch.flags));
/* Save the patch record itself */
/*
* Reset HC pointers in fixup records and save them.
*/
#ifdef VBOX_STRICT
uint32_t nrFixupRecs = 0;
AssertMsg((int32_t)nrFixupRecs == pPatch->patch.nrFixups, ("Fixup inconsistency! counted %d vs %d\n", nrFixupRecs, pPatch->patch.nrFixups));
#endif
#ifdef VBOX_STRICT
uint32_t nrLookupRecords = 0;
#endif
return VINF_SUCCESS;
}
/**
* Execute state save operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
*/
{
int rc;
/*
* Reset HC pointers that need to be recalculated when loading the state
*/
patmInfo.pGCStateHC = 0;
patmInfo.pvFaultMonitor = 0;
/*
* Count the number of patches in the tree (feeling lazy)
*/
RTAvloU32DoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, patmCountPatch, &patmInfo.savedstate.cPatches);
/*
* Save PATM structure
*/
/*
* Save patch memory contents
*/
/*
* Save GC state memory
*/
/*
* Save PATM stack page
*/
/*
* Save all patches
*/
/** @note patch statistics are not saved. */
return VINF_SUCCESS;
}
/**
* Execute state load operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
* @param u32Version Data layout version.
*/
{
int rc;
if ( u32Version != PATM_SSM_VERSION
#ifdef PATM_WITH_NEW_SSM
#else
)
#endif
{
}
/*
* Restore PATM structure
*/
#ifdef PATM_WITH_NEW_SSM
if (u32Version == PATM_SSM_VERSION_GETPUTMEM)
{
#endif
#ifdef PATM_WITH_NEW_SSM
}
else
{
}
#endif
/* Relative calls are made to the helper functions. Therefor their relative location must not change! */
/* Note: we reuse the saved global helpers and assume they are identical, which is kind of dangerous. */
if ( (pVM->patm.s.pfnHelperCallGC - pVM->patm.s.pPatchMemGC) != (patmInfo.pfnHelperCallGC - patmInfo.pPatchMemGC)
|| (pVM->patm.s.pfnHelperRetGC - pVM->patm.s.pPatchMemGC) != (patmInfo.pfnHelperRetGC - patmInfo.pPatchMemGC)
|| (pVM->patm.s.pfnHelperJumpGC - pVM->patm.s.pPatchMemGC) != (patmInfo.pfnHelperJumpGC - patmInfo.pPatchMemGC)
|| (pVM->patm.s.pfnHelperIretGC - pVM->patm.s.pPatchMemGC) != (patmInfo.pfnHelperIretGC - patmInfo.pPatchMemGC))
{
AssertMsgFailed(("Helper function ptrs don't match!!!\n"));
return VERR_SSM_INVALID_STATE;
}
{
AssertMsgFailed(("Patch memory ptrs and/or sizes don't match!!!\n"));
return VERR_SSM_INVALID_STATE;
}
/* Lowest and highest patched instruction */
/* Sysenter handlers */
/** @note patch statistics are not restored. */
/*
* Restore patch memory contents
*/
/*
* Restore GC state memory
*/
#ifdef PATM_WITH_NEW_SSM
if (u32Version == PATM_SSM_VERSION_GETPUTMEM)
{
#endif
#ifdef PATM_WITH_NEW_SSM
}
else
{
}
#endif
/*
* Restore PATM stack page
*/
/*
* Load all patches
*/
{
if (VBOX_FAILURE(rc))
{
AssertMsgFailed(("Out of memory!!!!\n"));
return VERR_NO_MEMORY;
}
/*
* Only restore the patch part of the tree record; not the internal data (except the key of course)
*/
Log(("Restoring patch %VRv -> %VRv\n", pPatchRec->patch.pPrivInstrGC, patmInfo.pPatchMemGC + pPatchRec->patch.pPatchBlockOffset));
{
{
/* We actually generated code for this patch. */
ret = RTAvloU32Insert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
AssertMsg(ret, ("Inserting patch %VGv offset %VGv failed!!\n", pPatchRec->patch.pPrivInstrGC, pPatchRec->CoreOffset.Key));
}
}
/* Set to zero as we don't need it anymore. */
/* The GC virtual ptr is fixed, but we must convert it manually again to HC. */
rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
/* Can fail due to page or page table not present. */
/*
* Restore fixup records and correct HC pointers in fixup records
*/
{
/* rec.pRelocPos now contains the relative position inside the hypervisor area. */
/* Convert to HC pointer again. */
{
{
Assert(pPatchRec->patch.cbPatchJump == SIZEOF_NEARJUMP32 || pPatchRec->patch.cbPatchJump == SIZEOF_NEAR_COND_JUMP32);
}
}
}
/* And all patch to guest lookup records */
Assert(pPatchRec->patch.nrPatch2GuestRecs || pPatchRec->patch.uState == PATCH_REFUSED || (pPatchRec->patch.flags & (PATMFL_SYSENTER_XP | PATMFL_IDTHANDLER | PATMFL_TRAPHANDLER | PATMFL_INT3_REPLACEMENT)));
{
for (uint32_t i=0;i<nrPatch2GuestRecs;i++)
{
patmr3AddP2GLookupRecord(pVM, &pPatchRec->patch, (uintptr_t)rec.Core.Key + pVM->patm.s.pPatchMemHC, rec.pOrgInstrGC, rec.enmType, rec.fDirty);
}
}
{
/* Insert the guest page lookup records (for detection self-modifying code) */
}
#if 0 /* can fail def LOG_ENABLED */
{
pPatchRec->patch.pTempInfo = (PPATCHINFOTEMP)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(PATCHINFOTEMP));
Log(("Patch code ----------------------------------------------------------\n"));
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(&pPatchRec->patch), PATCHCODE_PTR_GC(&pPatchRec->patch), patmr3DisasmCallback, &pPatchRec->patch);
Log(("Patch code ends -----------------------------------------------------\n"));
}
#endif
}
/*
* Correct absolute fixups in the global patch. (helper functions)
* Bit of a mess. Uses the new patch record, but restored patch functions.
*/
Log(("Correct fixups in global helper functions\n"));
while (true)
{
/* Get the record that's closest from above */
if (pRec == 0)
break;
/* rec.pRelocPos now contains the relative position inside the hypervisor area. */
/* Correct fixups that refer to PATM structures in the hypervisor region (their addresses might have changed). */
patmCorrectFixup(pVM, u32Version, patmInfo, &pVM->patm.s.pGlobalPatchRec->patch, pRec, offset, pFixup);
}
#ifdef VBOX_WITH_STATISTICS
/*
* Restore relevant old statistics
*/
#endif
return VINF_SUCCESS;
}
/**
* Correct fixups to predefined hypervisor PATM regions. (their addresses might have changed)
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param ulSSMVersion SSM version
* @param patmInfo Saved PATM structure
* @param pPatch Patch record
* @param pRec Relocation record
* @param pFixup Fixup address
*/
static void patmCorrectFixup(PVM pVM, unsigned ulSSMVersion, PATM &patmInfo, PPATCHINFO pPatch, PRELOCREC pRec, int32_t offset, RTRCPTR *pFixup)
{
{
case FIXUP_ABSOLUTE:
{
break;
{
LogFlow(("Changing absolute GCState at %VRv from %VRv to %VRv\n", patmInfo.pPatchMemGC + offset, *pFixup, (*pFixup - patmInfo.pGCStateGC) + pVM->patm.s.pGCStateGC));
}
else
{
LogFlow(("Changing absolute CPUMCTX at %VRv from %VRv to %VRv\n", patmInfo.pPatchMemGC + offset, *pFixup, (*pFixup - patmInfo.pCPUMCtxGC) + pVM->patm.s.pCPUMCtxGC));
/* The CPUMCTX structure has completely changed, so correct the offsets too. */
if (ulSSMVersion == PATM_SSM_VERSION_VER16)
{
/* ''case RT_OFFSETOF()'' does not work as gcc refuses to use & as a constant expression.
* Defining RT_OFFSETOF as __builtin_offsetof for gcc would make this possible. But this
* function is not available in older gcc versions, at least not in gcc-3.3 */
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
else
}
else
}
else
{
LogFlow(("Changing absolute Stats at %VRv from %VRv to %VRv\n", patmInfo.pPatchMemGC + offset, *pFixup, (*pFixup - patmInfo.pStatsGC) + pVM->patm.s.pStatsGC));
}
else
{
LogFlow(("Changing absolute Stack at %VRv from %VRv to %VRv\n", patmInfo.pPatchMemGC + offset, *pFixup, (*pFixup - patmInfo.pGCStackGC) + pVM->patm.s.pGCStackGC));
}
else
{
LogFlow(("Changing absolute PatchMem at %VRv from %VRv to %VRv\n", patmInfo.pPatchMemGC + offset, *pFixup, (*pFixup - patmInfo.pPatchMemGC) + pVM->patm.s.pPatchMemGC));
}
else
/* Note: rather assumptive! */
{
LogFlow(("Changing fForcedActions fixup from %x to %x\n", *pFixup, pVM->pVMGC + RT_OFFSETOF(VM, fForcedActions)));
}
else
{
static int cCpuidFixup = 0;
#ifdef LOG_ENABLED
#endif
/* very dirty assumptions about the cpuid patch and cpuid ordering. */
switch(cCpuidFixup & 3)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
cCpuidFixup++;
}
else
#ifdef RT_OS_WINDOWS
#endif
break;
}
case FIXUP_REL_JMPTOPATCH:
{
{
Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
{
}
else
#endif
{
oldJump[0] = 0xE9;
}
else
{
break;
}
/*
* Read old patch jump and compare it to the one we previously installed
*/
{
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
}
else
{
Log(("PATM: Patch jump was overwritten -> disabling patch!!\n"));
/*
* Disable patch; this is not a good solution
*/
/* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
}
else
if (VBOX_SUCCESS(rc))
{
}
else
{
}
}
else
{
Log(("Skip the guest jump to patch code for this disabled patch %08X - %08X\n", pPatch->pPrivInstrHC, pRec->pRelocPos));
}
break;
}
case FIXUP_REL_JMPTOGUEST:
{
Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
break;
}
}
}