CSAM.cpp revision c3fa5fcf137b8ec949ac2f868a83a0866b920663
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * CSAM - Guest OS Code Scanning and Analysis Manager
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * Copyright (C) 2006-2013 Oracle Corporation
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * This file is part of VirtualBox Open Source Edition (OSE), as
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * available from http://www.virtualbox.org. This file is free software;
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * you can redistribute it and/or modify it under the terms of the GNU
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * General Public License (GPL) as published by the Free Software
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * Foundation, in version 2 as it comes in the "COPYING" file of the
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/*******************************************************************************
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross* Header Files *
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross*******************************************************************************/
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/* Enabled by default */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/* Enable to monitor code pages for self-modifying code. */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/* Enable to monitor all scanned pages
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross#define CSAM_MONITOR_CSAM_CODE_PAGES */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/* Enable to scan beyond ret instructions.
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross#define CSAM_ANALYSE_BEYOND_RET */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/*******************************************************************************
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross* Internal Functions *
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross*******************************************************************************/
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossint csamR3CheckPageRecord(PVM pVM, RTRCPTR pInstr);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t opsize, bool fScanned);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/** @todo Temporary for debugging. */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic bool fInCSAMCodePageInvalidate = false;
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/*******************************************************************************
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross* Global Variables *
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross*******************************************************************************/
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Rossstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Rossstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross/** Command descriptors. */
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross { "csamon", 0, 0, NULL, 0, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross { "csamoff", 0, 0, NULL, 0, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross * SSM descriptor table for the CSAM structure.
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross /** @todo there are more fields that can be ignored here. */
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_PAD_HC64( CSAM, Alignment0, sizeof(uint32_t)),
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_RCPTR( CSAM, pPDBitmapGC), /// @todo ignore this?
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_RCPTR( CSAM, pPDHCBitmapGC), /// @todo ignore this?
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_IGN_HCPTR( CSAM, savedstate.pSSM),
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY( CSAM, savedstate.cPatchPageRecords),
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyBasePage),
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyFaultPage),
613a2f6ba31e891e3d947a356daf5e563d43c1ceGordon Ross SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvPossibleCodePage),
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvCallInstruction),
6b2bcd8e40cb530c97e59f351ceccb5c807ac34aGordon Ross SSMFIELD_ENTRY_IGNORE( CSAM, StatNrRemovedPages),
int rc;
rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTRCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageRemoveREMFlush,STAMTYPE_COUNTER, "/CSAM/PageRec/Removed/REMFlush", STAMUNIT_OCCURENCES, "The number of removed CSAM page records that caused a REM flush.");
STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
bool fEnabled;
#ifdef CSAM_ENABLE
fEnabled = true;
fEnabled = false;
if (fEnabled)
#ifdef VBOX_WITH_DEBUGGER
static bool fRegisteredCmds = false;
if (!fRegisteredCmds)
fRegisteredCmds = true;
return VINF_SUCCESS;
return VINF_SUCCESS;
if (offDelta)
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
int rc;
for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
return VINF_SUCCESS;
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
if (pPageRec)
return VINF_SUCCESS;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
int rc;
for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
return VINF_SUCCESS;
int rc;
rc = SSMR3GetStructEx(pSSM, &csamInfo, sizeof(csamInfo), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamFields[0], NULL);
memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
return rc;
rc = SSMR3GetStructEx(pSSM, &page, sizeof(page), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamPageRecFields[0], NULL);
pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
return VINF_SUCCESS;
static uint8_t *csamR3GCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
int rc;
void *pHCPtr;
if (pHCPtr)
return NULL;
typedef struct CSAMDISINFO
static DECLCALLBACK(int) csamR3ReadBytes(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead)
int rc = PATMR3ReadOrgInstr(pDisInfo->pVM, pDis->uInstrAddr + offInstr, &pDis->abInstr[offInstr], cbRead, &cbRead);
return rc;
|| PATMIsPatchGCAddr(pDisInfo->pVM, uSrcAddr) /** @todo does CSAM actually analyze patch code, or is this just a copy&past check? */
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pDisInfo->pVM), &pDis->abInstr[offInstr], uSrcAddr, cbMinRead);
return rc;
#ifdef DEBUG
if (pszOutput)
return DISInstrEx(InstrGC, enmCpuMode, ~(DISOPTYPE_INVALID | DISOPTYPE_HARMLESS | DISOPTYPE_RRM_MASK),
static int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC,
int rc;
case OP_INT:
return VINF_SUCCESS;
case OP_ILLUD2:
case OP_RETN:
case OP_INT3:
case OP_INVALID:
case OP_IRET:
return VINF_SUCCESS;
case OP_PUSHF:
case OP_POPF:
case OP_CLI:
case OP_PUSH:
case OP_STR:
case OP_LSL:
case OP_LAR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_CPUID:
case OP_IRET:
#ifdef DEBUG
case OP_STR:
case OP_LSL:
case OP_LAR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_CPUID:
case OP_PUSH:
case OP_IRET:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
case OP_JMP:
case OP_CALL:
#ifdef DEBUG
case OP_JMP:
case OP_CALL:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
#ifdef CSAM_ANALYSE_BEYOND_RET
static int csamAnalyseCallCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
int rc;
uint32_t i;
rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
int rc2;
#ifdef DEBUG
goto done;
goto done;
#ifdef DEBUG
rc2 = csamR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, (fCode32) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
rc2 = csamR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, (fCode32) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
goto done;
goto done;
case OP_NOP:
case OP_INT3:
case OP_LEA:
goto next_function;
case OP_PUSH:
goto next_function;
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
goto next_function;
case OP_SUB:
goto next_function;
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
goto next_function;
goto next_function;
done:
return rc;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
static int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
int rc2;
#ifdef DEBUG
goto done;
goto done;
goto done;
#ifdef DEBUG
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
goto done;
goto next_please;
AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
goto next_please;
#ifdef CSAM_ANALYSE_BEYOND_RET
/* Remember the address of the instruction following the ret in case the parent instruction was a call. */
pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + cbInstr;
goto done;
// For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
if ( ((cpu.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->fParam1) == OP_PARM_J))
|| (cpu.pCurInstr->uOpcode == OP_CALL && cpu.Param1.fUse == DISUSE_DISPLACEMENT32)) /* simple indirect call (call dword ptr [address]) */
/* We need to parse 'call dword ptr [address]' type of calls to catch cpuid instructions in some recent Linux distributions (e.g. OpenSuse 10.3) */
addr = 0;
if (addr == 0)
goto next_please;
goto done;
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
goto done;
goto done;
} //if ((cpu.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->fParam1) == OP_PARM_J))
#ifdef CSAM_SCAN_JUMP_TABLE
&& (cpu.Param1.fUse & (DISUSE_DISPLACEMENT32|DISUSE_INDEX|DISUSE_SCALE)) == (DISUSE_DISPLACEMENT32|DISUSE_INDEX|DISUSE_SCALE)
int rc2;
goto done;
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
goto done;
goto done;
done:
return rc;
int rc;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
int rc;
return VINF_SUCCESS;
* Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
return VWRN_CSAM_PAGE_NOT_FOUND;
return rc;
return VINF_SUCCESS;
if (pPageRec)
return VINF_SUCCESS;
Log(("CSAMR3FlushPage: page %RRv has changed -> FLUSH (rc=%Rrc) (Phys: %RGp vs %RGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
if (fRemovePage)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
return VWRN_CSAM_PAGE_NOT_FOUND;
int rc;
if (pPageRec)
return VINF_SUCCESS;
return VWRN_CSAM_PAGE_NOT_FOUND;
if (pPageRec)
return VWRN_CSAM_PAGE_NOT_FOUND;
return VINF_SUCCESS;
static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
int rc;
bool ret;
return NULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
#ifdef CSAM_MONITOR_CODE_PAGES
switch (enmTag)
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
case CSAM_TAG_CSAM:
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
(fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", GCPtr, rc));
#ifdef VBOX_WITH_STATISTICS
switch (enmTag)
case CSAM_TAG_CSAM:
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
if (fMonitorInvalidation)
int rc;
bool fMonitorInvalidation;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return VINF_SUCCESS;
#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
#ifdef CSAM_MONITOR_CODE_PAGES
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
(fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", pPageAddrGC, rc));
// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %RRv (%RHp) aren't readonly (%RX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
/* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
return VINF_SUCCESS;
#ifdef VBOX_STRICT
if (pPageRec)
#ifdef CSAM_MONITOR_CODE_PAGES
#ifdef VBOX_WITH_STATISTICS
case CSAM_TAG_CSAM:
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
AssertFailed();
return VINF_SUCCESS;
static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
int rc;
return VINF_PGM_HANDLER_DO_DEFAULT;
rc = VMR3ReqCallVoidNoWait(pVM, VMCPUID_ANY, (PFNRT)CSAMDelayedWriteHandler, 3, pVM, (RTRCPTR)GCPtr, cbBuf);
return VINF_PGM_HANDLER_DO_DEFAULT;
fInCSAMCodePageInvalidate = true;
/** @todo We can't remove the page (which unregisters the virtual handler) as we are called from a DoWithAll on the virtual handler tree. Argh. */
fInCSAMCodePageInvalidate = false;
return VINF_SUCCESS;
if (pPageRec)
if (fScanned)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
return rc;
return VINF_SUCCESS;
int rc;
#ifdef VBOX_WITH_REM
Log(("CSAMR3FlushDirtyPages: flush %RRv (modifypage rc=%Rrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
#ifdef VBOX_WITH_RAW_MODE
int rc;
return VINF_SUCCESS;
return VERR_INVALID_PARAMETER;
if (pHandler)
return VERR_INVALID_PARAMETER;
return rc;
&& (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
Log(("CSAMCheckGates: check gate %d failed due to rc %Rrc GCPtrBase=%RRv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
switch (iGate) {
Log(("Installing %s gate handler for 0x%X at %RRv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
if (pNewHandlerGC)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)