CSAM.cpp revision 34787f0edc10c141707921c6b4f11c4f91be4c7c
/* $Id$ */
/** @file
* CSAM - Guest OS Code Scanning and Analysis Manager
*/
/*
* Copyright (C) 2006-2007 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_CSAM
#include "CSAMInternal.h"
#include <VBox/disopcode.h>
/* Enabled by default */
#define CSAM_ENABLE
/* Enable to monitor code pages for self-modifying code. */
#define CSAM_MONITOR_CODE_PAGES
/* Enable to monitor all scanned pages
#define CSAM_MONITOR_CSAM_CODE_PAGES */
/* Enable to scan beyond ret instructions.
#define CSAM_ANALYSE_BEYOND_RET */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
static int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
/** @todo Temporary for debugging. */
static bool fInCSAMCodePageInvalidate = false;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
/** Command descriptors. */
{
/* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
};
#endif
/**
* SSM descriptor table for the CSAM structure.
*/
static const SSMFIELD g_aCsamFields[] =
{
/** @todo there are more fields that can be ignored here. */
};
/** Fake type to simplify g_aCsamPDBitmapArray construction. */
typedef struct
{
/**
* SSM descriptor table for the CSAM::pPDBitmapHC array.
*/
static SSMFIELD const g_aCsamPDBitmapArray[] =
{
};
/**
* SSM descriptor table for the CSAMPAGEREC structure.
*/
static const SSMFIELD g_aCsamPageRecFields[] =
{
SSMFIELD_ENTRY_PAD_HC_AUTO( 0, 4),
SSMFIELD_ENTRY_PAD_HC_AUTO( 0, 4),
};
/**
* Initializes the CSAM.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc;
LogFlow(("CSAMR3Init\n"));
/* Allocate bitmap for the page directory. */
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);
/*
* Register save and load state notifiers.
*/
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;
if (RT_FAILURE(rc))
#ifdef CSAM_ENABLE
fEnabled = true;
#else
fEnabled = false;
#endif
if (fEnabled)
#ifdef VBOX_WITH_DEBUGGER
/*
* Debugger commands.
*/
static bool fRegisteredCmds = false;
if (!fRegisteredCmds)
{
if (RT_SUCCESS(rc))
fRegisteredCmds = true;
}
#endif
return VINF_SUCCESS;
}
/**
* (Re)initializes CSAM
*
* @param pVM The VM.
*/
{
/*
* Assert alignment and sizes.
*/
/*
* Setup any fixed pointers and offsets.
*/
/* not necessary */
/** @note never mess with the pgdir bitmap here! */
return VINF_SUCCESS;
}
/**
* Applies relocations to data and code managed by this
* component. This function will be called at init and
* whenever the VMM need to relocate itself inside the GC.
*
* The csam will update the addresses used by the switcher.
*
* @param pVM The VM.
* @param offDelta Relocation delta.
*/
{
if (offDelta)
{
/* Adjust pgdir and page bitmap pointers. */
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
{
{
}
}
}
return;
}
/**
* Terminates the csam.
*
* Termination means cleaning up and freeing all resources,
* the VM it self is at this point powered off or suspended.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc;
/* @todo triggers assertion in MMHyperFree */
#if 0
for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
{
}
#endif
return VINF_SUCCESS;
}
/**
* CSAM reset callback.
*
* @returns VBox status code.
* @param pVM The VM which is reset.
*/
{
/* Clear page bitmaps. */
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
{
{
}
}
/* Remove all CSAM page records. */
while(true)
{
if (pPageRec)
{
}
else
break;
}
return VINF_SUCCESS;
}
/**
* Callback function for RTAvlPVDoWithAll
*
* Counts the number of records in the tree
*
* @returns VBox status code.
* @param pNode Current node
* @param pcPatches Pointer to patch counter
*/
{
return VINF_SUCCESS;
}
/**
* Callback function for RTAvlPVDoWithAll
*
* Saves the state of the page record
*
* @returns VBox status code.
* @param pNode Current node
* @param pVM1 VM Handle
*/
{
int rc;
/* Save the page record itself */
{
}
return VINF_SUCCESS;
}
/**
* Execute state save operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
*/
{
int rc;
/*
* Count the number of page records in the tree (feeling lazy)
*/
/*
* Save CSAM structure
*/
/* Save pgdir bitmap */
for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
{
if(csamInfo.pPDBitmapHC[i])
{
/* Save the page bitmap. */
}
}
/*
* Save page records
*/
/** @note we don't restore aDangerousInstr; it will be recreated automatically. */
return VINF_SUCCESS;
}
/**
* Execute state load operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
* @param uVersion Data layout version.
* @param uPass The data pass.
*/
{
int rc;
if (uVersion != CSAM_SSM_VERSION)
{
}
/*
* Restore CSAM structure
*/
#if 0
#else
rc = SSMR3GetStructEx(pSSM, &csamInfo, sizeof(csamInfo), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamFields[0], NULL);
#endif
/* Restore dirty code page info. */
memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
/* Restore possible code page */
memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
/* Restore pgdir bitmap (we'll change the pointers next). */
#if 0
#else
#endif
/*
* Restore page bitmaps
*/
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]);
if (RT_FAILURE(rc))
{
return rc;
}
/* Convert to GC pointer. */
/* Restore the bitmap. */
}
else
{
}
}
/*
* Restore page records
*/
{
#if 0
#else
rc = SSMR3GetStructEx(pSSM, &page, sizeof(page), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamPageRecFields[0], NULL);
#endif
/*
* Recreate the page record
*/
pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
{
}
else
{
}
}
/* Note: we don't restore aDangerousInstr; it will be recreated automatically. */
return VINF_SUCCESS;
}
/**
* Convert guest context address to host context pointer
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCacheRec Address conversion cache record
* @param pGCPtr Guest context pointer
* @returns Host context pointer or NULL in case of an error
*
*/
static R3PTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
{
int rc;
if (pHCPtr)
return pHCPtr;
if (pCacheRec->pPageLocStartHC)
{
{
}
}
/* Release previous lock if any. */
{
}
if (rc != VINF_SUCCESS)
{
//// AssertMsgRC(rc, ("MMR3PhysGCVirt2HCVirtEx failed for %RRv\n", pGCPtr));
return NULL;
}
return pHCPtr;
}
/**
* Read callback for disassembly function; supports reading bytes that cross a page boundary
*
* @returns VBox status code.
* @param pSrc GC source pointer
* @param pDest HC destination pointer
* @param size Number of bytes to read
* @param dwUserdata Callback specific user data (pCpu)
*
*/
static DECLCALLBACK(int) CSAMR3ReadBytes(RTUINTPTR pSrc, uint8_t *pDest, unsigned size, void *pvUserdata)
{
/* We are not interested in patched instructions, so read the original opcode bytes. */
/** @note single instruction patches (int3) are checked in CSAMR3AnalyseCallback */
for (int i=0;i<orgsize;i++)
{
if (RT_SUCCESS(rc))
{
pSrc++;
pDest++;
size--;
}
else
break;
}
if (size == 0)
return VINF_SUCCESS;
{
}
else
{
/* pInstrHC is the base address; adjust according to the GC pointer. */
}
return VINF_SUCCESS;
}
inline int CSAMR3DISInstr(PVM pVM, DISCPUSTATE *pCpu, RTRCPTR InstrGC, uint8_t *InstrHC, uint32_t *pOpsize, char *pszOutput)
{
(pCpu)->apvUserData[2] = (void *)(uintptr_t)InstrGC; Assert(sizeof(InstrGC) <= sizeof(pCpu->apvUserData[0]));
#ifdef DEBUG
#else
/* We are interested in everything except harmless stuff */
return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, ~(OPTYPE_INVALID | OPTYPE_HARMLESS | OPTYPE_RRM_MASK));
#endif
}
/**
* Analyses the instructions following the cli for compliance with our heuristics for cli
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCpu CPU disassembly state
* @param pInstrGC Guest context pointer to privileged instruction
* @param pCurInstrGC Guest context pointer to the current instruction
* @param pCacheRec GC to HC cache record
* @param pUserData User pointer (callback specific)
*
*/
static int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC,
{
int rc;
{
case OP_INT:
{
//two byte int 3
return VINF_SUCCESS;
}
break;
case OP_ILLUD2:
/* This appears to be some kind of kernel panic in Linux 2.4; no point to continue. */
case OP_RETN:
case OP_INT3:
case OP_INVALID:
#if 1
/* removing breaks win2k guests? */
case OP_IRET:
#endif
return VINF_SUCCESS;
}
// Check for exit points
{
/* It's not a good idea to patch pushf instructions:
* - increases the chance of conflicts (code jumping to the next instruction)
* - better to patch the cli
* - code that branches before the cli will likely hit an int 3
* - in general doesn't offer any benefits as we don't allow nested patch blocks (IF is always 1)
*/
case OP_PUSHF:
case OP_POPF:
break;
case OP_CLI:
{
/* Make sure the instructions that follow the cli have not been encountered before. */
while (true)
{
break;
{
/* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
break;
}
pCurInstrGC += opsize;
{ /* Force pCurInstrHC out of scope after we stop using it (page lock!) */
uint8_t *pCurInstrHC = 0;
if (pCurInstrHC == NULL)
{
break;
}
}
if (RT_FAILURE(rc))
break;
}
break;
}
case OP_PUSH:
break;
/* no break */
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:
break;
case OP_LSL:
break;
case OP_LAR:
break;
case OP_SGDT:
break;
case OP_SLDT:
break;
case OP_SIDT:
break;
case OP_SMSW:
break;
case OP_VERW:
break;
case OP_VERR:
break;
case OP_CPUID:
break;
case OP_PUSH:
break;
case OP_IRET:
break;
}
#endif
{
if (RT_FAILURE(rc))
{
return VWRN_CONTINUE_ANALYSIS;
}
}
return VINF_SUCCESS; /* Look no further in this branch. */
return VWRN_CONTINUE_ANALYSIS;
case OP_JMP:
case OP_CALL:
{
{
#ifdef DEBUG
{
case OP_JMP:
break;
case OP_CALL:
break;
}
#endif
return VWRN_CONTINUE_ANALYSIS;
}
return VWRN_CONTINUE_ANALYSIS;
}
}
return VWRN_CONTINUE_ANALYSIS;
}
#ifdef CSAM_ANALYSE_BEYOND_RET
/**
* Wrapper for csamAnalyseCodeStream for call instructions.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pInstrGC Guest context pointer to privileged instruction
* @param pCurInstrGC Guest context pointer to the current instruction
* @param fCode32 16 or 32 bits code
* @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
* @param pUserData User pointer (callback specific)
*
*/
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);
for (i=0;i<CallExitRec.cInstrAfterRet;i++)
{
/* Check if we've previously encountered the instruction after the ret. */
{
int rc2;
#ifdef DEBUG
char szOutput[256];
#endif
{
/* New address; let's take a look at it. */
{
rc = VERR_NO_MEMORY;
goto done;
}
}
/**
* Some generic requirements for recognizing an adjacent function:
* - alignment fillers that consist of:
* - nop
* - lea genregX, [genregX (+ 0)]
* - push ebp after the filler (can extend this later); aligned at at least a 4 byte boundary
*/
for (int j=0;j<16;j++)
{
if (pCurInstrHC == NULL)
{
goto done;
}
#ifdef DEBUG
#else
#endif
if (RT_FAILURE(rc2))
{
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
goto done;
}
{
{
/// @todo fault in the page
goto done;
}
//all is fine, let's continue
}
{
case OP_NOP:
case OP_INT3:
break; /* acceptable */
case OP_LEA:
/* Must be similar to:
*
* lea esi, [esi]
* lea esi, [esi+0]
* Any register is allowed as long as source and destination are identical.
*/
)
)
)
{
goto next_function;
}
break;
case OP_PUSH:
{
if ( (pCurInstrGC & 0x3) != 0
)
{
goto next_function;
}
{
/* Analyse the function. */
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
}
goto next_function;
}
case OP_SUB:
{
if ( (pCurInstrGC & 0x3) != 0
)
{
goto next_function;
}
{
/* Analyse the function. */
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
}
goto next_function;
}
default:
goto next_function;
}
/* Mark it as scanned. */
pCurInstrGC += opsize;
} /* for at most 16 instructions */
; /* MSVC complains otherwise */
}
}
done:
return rc;
}
#else
#endif
/**
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pInstrGC Guest context pointer to privileged instruction
* @param pCurInstrGC Guest context pointer to the current instruction
* @param fCode32 16 or 32 bits code
* @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
* @param pUserData User pointer (callback specific)
*
*/
static int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
{
int rc = VWRN_CONTINUE_ANALYSIS;
int rc2;
#ifdef DEBUG
char szOutput[256];
#endif
/*
* Limit the call depth. (rather arbitrary upper limit; too low and we won't detect certain
* cpuid instructions in Linux kernels; too high and we waste too much time scanning code)
* (512 is necessary to detect cpuid instructions in Red Hat EL4; see defect 1355)
* @note we are using a lot of stack here. couple of 100k when we go to the full depth (!)
*/
{
return VINF_SUCCESS; //let's not go on forever
}
while(rc == VWRN_CONTINUE_ANALYSIS)
{
{
{
/* New address; let's take a look at it. */
{
rc = VERR_NO_MEMORY;
goto done;
}
}
}
else
{
rc = VINF_SUCCESS;
goto done;
}
{ /* Force pCurInstrHC out of scope after we stop using it (page lock!) */
if (pCurInstrHC == NULL)
{
goto done;
}
#ifdef DEBUG
#else
#endif
}
if (RT_FAILURE(rc2))
{
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
rc = VINF_SUCCESS;
goto done;
}
{
{
/// @todo fault in the page
goto next_please;
}
//all is fine, let's continue
}
/*
* If it's harmless, then don't bother checking it (the disasm tables had better be accurate!)
*/
{
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. */
if ( pCacheRec->pCallExitRec
{
pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + opsize;
}
#endif
if (rc == VINF_SUCCESS)
goto done;
// For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
if ( ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
|| (cpu.pCurInstr->opcode == OP_CALL && cpu.param1.flags == USE_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;
}
else
if (addr == 0)
{
rc = VINF_SUCCESS;
break;
}
/* If the target address lies in a patch generated jump, then special action needs to be taken. */
/* Same page? */
{
{
goto next_please;
}
/* All is fine, let's continue. */
}
{
{
/* New branch target; let's take a look at it. */
{
rc = VERR_NO_MEMORY;
goto done;
}
}
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
else
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
if (rc != VINF_SUCCESS) {
goto done;
}
}
{//unconditional jump; return to caller
rc = VINF_SUCCESS;
goto done;
}
} //if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
#ifdef CSAM_SCAN_JUMP_TABLE
else
&& (cpu.param1.flags & (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)) == (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)
)
{
int rc2;
Log(("Jump through jump table\n"));
if (rc2 == VINF_SUCCESS)
{
for (uint32_t i=0;i<2;i++)
{
/* Same page? */
break;
if ( rc2 != VINF_SUCCESS
|| (fFlags & X86_PTE_US)
)
break;
{
{
/* New branch target; let's take a look at it. */
{
rc = VERR_NO_MEMORY;
goto done;
}
}
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
if (rc != VINF_SUCCESS) {
goto done;
}
}
}
}
}
#endif
if (rc != VWRN_CONTINUE_ANALYSIS) {
break; //done!
}
{
rc = VINF_SUCCESS;
goto done;
}
pCurInstrGC += opsize;
}
done:
return rc;
}
/**
* Calculates the 64 bits hash value for the current page
*
* @returns hash value
* @param pVM The VM to operate on.
* @param pInstr Page address
*/
{
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;
}
// don't want to get division by zero traps
}
/**
* Notify CSAM of a page flush
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param addr GC address of the page to flush
* @param fRemovePage Page removal flag
*/
{
int rc;
if (!CSAMIsEnabled(pVM))
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;
}
/* Returned at a very early stage (no paging yet presumably). */
if (rc == VERR_NOT_SUPPORTED)
{
return rc;
}
if (RT_SUCCESS(rc))
{
if ( (fFlags & X86_PTE_US)
)
{
/* User page -> not relevant for us. */
return VINF_SUCCESS;
}
}
else
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)
else
{
if (rc == VINF_SUCCESS)
{
return VERR_NO_MEMORY;
}
else
}
/*
* Inform patch manager about the flush; no need to repeat the above check twice.
*/
return VINF_SUCCESS;
}
else
{
return VWRN_CSAM_PAGE_NOT_FOUND;
}
}
/**
* Notify CSAM of a page flush
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param addr GC address of the page to flush
*/
{
}
/**
* Remove a CSAM monitored page. Use with care!
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param addr GC address of the page to flush
*/
{
int rc;
if (pPageRec)
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
}
return VWRN_CSAM_PAGE_NOT_FOUND;
}
/**
* Check a page record in case a page has been changed
*
* @returns VBox status code. (trap handled or not)
* @param pVM The VM to operate on.
* @param pInstrGC GC instruction pointer
*/
{
if (pPageRec)
{
}
else
return VWRN_CSAM_PAGE_NOT_FOUND;
return VINF_SUCCESS;
}
/**
* Returns monitor description based on CSAM tag
*
* @return description string
* @param enmTag Owner tag
*/
{
if (enmTag == CSAM_TAG_PATM)
return "CSAM-PATM self-modifying code monitor handler";
else
if (enmTag == CSAM_TAG_REM)
return "CSAM-REM self-modifying code monitor handler";
return "CSAM self-modifying code monitor handler";
}
/**
* Adds page record to our lookup tree
*
* @returns CSAMPAGE ptr or NULL if failure
* @param pVM The VM to operate on.
* @param GCPtr Page address
* @param enmTag Owner tag
* @param fCode32 16 or 32 bits code
* @param fMonitorInvalidation Monitor page invalidation flag
*/
static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
{
int rc;
bool ret;
{
AssertMsgFailed(("csamCreatePageRecord: Out of memory!!!!\n"));
return NULL;
}
/* Round down to page boundary. */
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:
#endif
{
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));
if (RT_FAILURE(rc))
/* Could fail, because it's already monitored. Don't treat that condition as fatal. */
/* Prefetch it in case it's not there yet. */
break;
}
default:
break; /* to shut up GCC */
}
#ifdef VBOX_WITH_STATISTICS
switch (enmTag)
{
case CSAM_TAG_CSAM:
break;
case CSAM_TAG_PATM:
break;
case CSAM_TAG_REM:
break;
default:
break; /* to shut up GCC */
}
#endif
#endif
if (fMonitorInvalidation)
}
/**
* Monitors a code page (if not already monitored)
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param pPageAddrGC The page to monitor
* @param enmTag Monitor tag
*/
{
int rc;
bool fMonitorInvalidation;
/* Dirty pages must be handled before calling this function!. */
return VINF_SUCCESS; /* too early */
/** @todo implicit assumption */
{
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
if ( rc == VINF_SUCCESS
&& (fFlags & X86_PTE_US))
{
/* We don't care about user pages. */
return VINF_SUCCESS;
}
}
/** @todo reference count */
#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
#endif
#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));
if (RT_FAILURE(rc))
/* Could fail, because it's already monitored. Don't treat that condition as fatal. */
/* Prefetch it in case it's not there yet. */
}
else
{
/* Prefetch it in case it's not there yet. */
/* Make sure it's readonly. Page invalidation may have modified the attributes. */
}
#if 0 /* def VBOX_STRICT -> very annoying) */
{
// AssertMsg( (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
// || !(fPageShw & X86_PTE_RW)
// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %RRv (%RHp) aren't readonly (%RX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
}
#endif
{
/* Prefetch it in case it's not there yet. */
/* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
}
#endif /* CSAM_MONITOR_CODE_PAGES */
return VINF_SUCCESS;
}
/**
* Unmonitors a code page
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param pPageAddrGC The page to monitor
* @param enmTag Monitor tag
*/
{
#ifdef VBOX_STRICT
#endif
}
/**
* Removes a page record from our lookup tree
*
* @returns VBox status code
* @param pVM The VM to operate on.
* @param GCPtr Page address
*/
{
if (pPageRec)
{
#ifdef CSAM_MONITOR_CODE_PAGES
{
/* @todo -> this is expensive (cr3 reload)!!!
* if this happens often, then reuse it instead!!!
*/
}
{
/* Make sure the recompiler flushes its cache as this page is no longer monitored. */
}
#endif
#ifdef VBOX_WITH_STATISTICS
{
case CSAM_TAG_CSAM:
break;
case CSAM_TAG_PATM:
break;
case CSAM_TAG_REM:
break;
default:
break; /* to shut up GCC */
}
#endif
}
else
AssertFailed();
return VINF_SUCCESS;
}
/**
* Callback for delayed writes from non-EMT threads
*
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
*/
{
}
/**
* \#PF Handler callback for virtual access handler ranges.
*
* Important to realize that a physical page in a range can have aliases, and
* for ALL and WRITE handlers these will also trigger.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
* @param pvPtr The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
int rc;
{
Log(("CSAMCodePageWriteHandler: dummy write -> ignore\n"));
return VINF_PGM_HANDLER_DO_DEFAULT;
}
{
}
else
{
/* Queue the write instead otherwise we'll get concurrency issues. */
/** @note in theory not correct to let it write the data first before disabling a patch!
* (if it writes the same data as the patch jump and we replace it with obsolete opcodes)
*/
Log(("CSAMCodePageWriteHandler: delayed write!\n"));
rc = VMR3ReqCallVoidNoWait(pVM, VMCPUID_ANY, (PFNRT)CSAMDelayedWriteHandler, 3, pVM, (RTRCPTR)GCPtr, cbBuf);
}
return VINF_PGM_HANDLER_DO_DEFAULT;
}
/**
* \#PF Handler callback for invalidation of virtual access handler ranges.
*
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest has changed.
*/
{
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;
}
/**
* Check if the current instruction has already been checked before
*
* @returns VBox status code. (trap handled or not)
* @param pVM The VM to operate on.
* @param pInstr Instruction pointer
* @param pPage CSAM patch structure pointer
*/
{
{
{
return true;
}
return false;
}
if (pPageRec)
{
{
return true;
}
}
else
{
}
return false;
}
/**
*
* @param pVM The VM to operate on.
* @param pPage Patch structure pointer
* @param pInstr Instruction pointer
* @param opsize Instruction size
* @param fScanned Mark as scanned or not
*/
{
/** @todo should recreate empty bitmap if !fScanned */
return;
if (fScanned)
{
// retn instructions can be scanned more than once
{
}
{
}
else
}
else
}
/**
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pInstr Instruction pointer
* @param opsize Instruction size
* @param fScanned Mark as scanned or not
*/
{
{
return VINF_SUCCESS;
}
return VINF_SUCCESS;
}
/**
* Scan and analyse code
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCtxCore CPU context
* @param pInstrGC Instruction pointer
*/
{
{
// No use
return VINF_SUCCESS;
}
if (CSAMIsEnabled(pVM))
{
/* Assuming 32 bits code for now. */
Assert(SELMGetCpuModeFromSelector(pVM, pCtxCore->eflags, pCtxCore->cs, &pCtxCore->csHid) == CPUMODE_32BIT);
}
return VINF_SUCCESS;
}
/**
* Scan and analyse code
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pInstrGC Instruction pointer (0:32 virtual address)
*/
{
int rc;
if ( EMIsRawRing0Enabled(pVM) == false
{
/* Not active. */
return VINF_SUCCESS;
}
if (CSAMIsEnabled(pVM))
{
/* Cache record for CSAMGCVirtToHCVirt */
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
if (rc != VINF_SUCCESS)
{
return rc;
}
}
return VINF_SUCCESS;
}
/**
* Flush dirty code pages
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
{
int rc;
/* Notify the recompiler that this page has been changed. */
/* Enable write protection again. (use the fault address as it might be an alias) */
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));
if ( rc == VINF_SUCCESS
&& (fFlags & X86_PTE_US))
{
/* We don't care about user pages. */
}
}
}
return VINF_SUCCESS;
}
/**
* Flush potential new code pages
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
{
/* Resync the page to make sure instruction fetch will fault */
}
return VINF_SUCCESS;
}
/**
* Perform any pending actions
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pVCpu The VMCPU to operate on.
*/
{
return VINF_SUCCESS;
}
/**
* Analyse interrupt and trap gates
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param iGate Start gate
* @param cGates Number of gates to check
*/
{
int rc;
if (EMIsRawRing0Enabled(pVM) == false)
{
/* Enabling interrupt gates only works when raw ring 0 is enabled. */
//AssertFailed();
return VINF_SUCCESS;
}
/* We only check all gates once during a session */
&& cGates != 256)
return VINF_SUCCESS; /* too early */
/* We only check all gates once during a session */
&& cGates != 1)
return VINF_SUCCESS; /* ignored */
return VERR_INVALID_PARAMETER;
if (cGates != 1)
{
{
if (pHandler)
{
if (rc != VINF_SUCCESS)
{
continue;
}
}
}
}
/* Determine valid upper boundary. */
return VERR_INVALID_PARAMETER;
/*
* Get IDT entries.
*/
if (RT_FAILURE(rc))
{
return rc;
}
pGuestIdte = &aIDT[0];
{
&& (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
)
{
if ( RT_FAILURE(rc)
)
{
/* Refuse to patch a handler whose idt cs selector isn't wide open. */
Log(("CSAMCheckGates: check gate %d failed due to rc %Rrc GCPtrBase=%RRv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
continue;
}
{
Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
}
else
{
Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
}
if (rc != VINF_SUCCESS)
{
continue;
}
/* OpenBSD guest specific patch test. */
if (iGate >= 0x20)
{
0x2B, /* OpenBSD 4.0 installation ISO */
0x2F}; /* OpenBSD 4.0 after install */
for (unsigned i=0;i<RT_ELEMENTS(aOpenBsdPushCSOffset);i++)
{
if ( rc == VINF_SUCCESS
{
rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
if (RT_SUCCESS(rc))
Log(("Installed OpenBSD interrupt handler prefix instruction (push cs) patch\n"));
}
}
}
/* Trap gates and certain interrupt gates. */
else
switch (iGate) {
case 8:
case 10:
case 11:
case 12:
case 13:
case 14:
case 17:
break;
default:
/* No error code. */
break;
}
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)
{
if (RT_FAILURE(rc))
}
}
}
} /* for */
return VINF_SUCCESS;
}
/**
* Record previous call instruction addresses
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param GCPtrCall Call address
*/
{
{
return VINF_SUCCESS;
}
return VINF_SUCCESS;
}
/**
*
* @returns 0 - disabled, 1 - enabled
* @param pVM The VM to operate on.
*/
{
return pVM->fCSAMEnabled;
}
#ifdef VBOX_WITH_DEBUGGER
/**
* The '.csamoff' 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.
*/
static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
{
if (RT_FAILURE(rc))
}
/**
* The '.csamon' 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.
*/
static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
{
if (RT_FAILURE(rc))
}
#endif /* VBOX_WITH_DEBUGGER */