CSAM.cpp revision 8da94e4d6813b682cdb38eb9b151ada72d91b40e
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * CSAM - Guest OS Code Scanning and Analysis Manager
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * available from http://www.virtualbox.org. This file is free software;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * additional information or have any questions.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/*******************************************************************************
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync* Header Files *
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync*******************************************************************************/
afed5ab737f4aacfae3fe73776f40e989190a7cavboxsync/* Enabled by default */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/* Enable to monitor code pages for self-modifying code. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/* Enable to monitor all scanned pages
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync#define CSAM_MONITOR_CSAM_CODE_PAGES */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/* Enable to scan beyond ret instructions.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync#define CSAM_ANALYSE_BEYOND_RET */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/*******************************************************************************
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync* Internal Functions *
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync*******************************************************************************/
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t opsize, bool fScanned);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/** @todo Temporary for debugging. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic bool fInCSAMCodePageInvalidate = false;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/*******************************************************************************
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync* Global Variables *
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync*******************************************************************************/
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync/** Command descriptors. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync { "csamon", 0, 0, NULL, 0, NULL, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync { "csamoff", 0, 0, NULL, 0, NULL, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Initializes the CSAM.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
fdea543f71872a3ec3909536a4fce37ab7aa3a8bvboxsync /* Allocate bitmap for the page directory. */
fdea543f71872a3ec3909536a4fce37ab7aa3a8bvboxsync rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
fdea543f71872a3ec3909536a4fce37ab7aa3a8bvboxsync rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTRCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
fdea543f71872a3ec3909536a4fce37ab7aa3a8bvboxsync pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsync pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Register save and load state notificators.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = SSMR3RegisterInternal(pVM, "CSAM", 0, CSAM_SSM_VERSION, sizeof(pVM->csam.s) + PAGE_SIZE*16,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync 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.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
a11c569636fa6838bd423f4631a9660a5a84204bvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
a11c569636fa6838bd423f4631a9660a5a84204bvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
3ecf9412133496b2aeb090cfd33a286404ec59fbvboxsync STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Check CFGM option and enable/disable CSAM.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "CSAMEnabled", &fEnabled);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Debugger commands.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync static bool fRegisteredCmds = false;
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsync int rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * (Re)initializes CSAM
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Assert alignment and sizes.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertRelease(sizeof(pVM->csam.s) <= sizeof(pVM->csam.padding));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Setup any fixed pointers and offsets.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* not necessary */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync memset(pVM->csam.s.pvDirtyBasePage, 0, sizeof(pVM->csam.s.pvDirtyBasePage));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync memset(pVM->csam.s.pvDirtyFaultPage, 0, sizeof(pVM->csam.s.pvDirtyFaultPage));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync memset(pVM->csam.s.pvCallInstruction, 0, sizeof(pVM->csam.s.pvCallInstruction));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /** @note never mess with the pgdir bitmap here! */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Applies relocations to data and code managed by this
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * component. This function will be called at init and
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * whenever the VMM need to relocate itself inside the GC.
9782b553bdb12385214a3ac596aff1476bcb7cbdvboxsync * The csam will update the addresses used by the switcher.
e08de24d4792d31b7f2aac29db5cb8840d940009vboxsync * @param pVM The VM.
8a132edc1577cbe2a19cd778c1b2bea6ae5e8515vboxsync * @param offDelta Relocation delta.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(void) CSAMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Adjust pgdir and page bitmap pointers. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Terminates the csam.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Termination means cleaning up and freeing all resources,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * the VM it self is at this point powered off or suspended.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* @todo triggers assertion in MMHyperFree */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * CSAM reset callback.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM which is reset.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Clear page bitmaps. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync ASMMemZero32(pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
0e77737b0ba913683e614db11463b31ca67aacbevboxsync /* Remove all CSAM page records. */
0e77737b0ba913683e614db11463b31ca67aacbevboxsync while(true)
0e77737b0ba913683e614db11463b31ca67aacbevboxsync PCSAMPAGEREC pPageRec = (PCSAMPAGEREC)RTAvlPVGetBestFit(&pVM->csam.s.pPageTree, 0, true);
2d53f6e472561965d363674e17f48d3bdffc24d3vboxsync#define CSAM_SUBTRACT_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) - (uintptr_t)(b)
2d53f6e472561965d363674e17f48d3bdffc24d3vboxsync#define CSAM_ADD_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) + (uintptr_t)(b)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Callback function for RTAvlPVDoWithAll
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Counts the number of records in the tree
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pNode Current node
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pcPatches Pointer to patch counter
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsyncstatic DECLCALLBACK(int) CountRecord(PAVLPVNODECORE pNode, void *pcPatches)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync *(uint32_t *)pcPatches = *(uint32_t *)pcPatches + 1;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Callback function for RTAvlPVDoWithAll
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Saves the state of the page record
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pNode Current node
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsync * @param pVM1 VM Handle
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) SavePageState(PAVLPVNODECORE pNode, void *pVM1)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Save the page record itself */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = SSMR3PutMem(pSSM, page.page.pBitmap, CSAM_PAGE_BITMAP_SIZE);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Execute state save operation.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsync * @param pVM VM Handle.
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsync * @param pSSM SSM operation handle.
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsyncstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM)
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync * Count the number of page records in the tree (feeling lazy)
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, CountRecord, &csamInfo.savedstate.cPageRecords);
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync * Save CSAM structure
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = SSMR3PutMem(pSSM, &csamInfo, sizeof(csamInfo));
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync /* Save pgdir bitmap */
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
440444d68cda7866c59e2e3d3f236608ef1c316fvboxsync /* Save the page bitmap. */
d98e61ba075ed7d0b567a5d884bc85d643fe3de7vboxsync rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * Save page records
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync rc = RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, SavePageState, pVM);
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * Execute state load operation.
5e797edc29f96c8367de4fbf5874171c24a89ba7vboxsync * @returns VBox status code.
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * @param pVM VM Handle.
5e797edc29f96c8367de4fbf5874171c24a89ba7vboxsync * @param pSSM SSM operation handle.
5e797edc29f96c8367de4fbf5874171c24a89ba7vboxsync * @param u32Version Data layout version.
8e8844a522f5d335f177a0313b03067d79cce201vboxsyncstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync AssertMsgFailed(("csamR3Load: Invalid version u32Version=%d!\n", u32Version));
e08de24d4792d31b7f2aac29db5cb8840d940009vboxsync * Restore CSAM structure
5e797edc29f96c8367de4fbf5874171c24a89ba7vboxsync rc = SSMR3GetMem(pSSM, &csamInfo, sizeof(csamInfo));
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync pVM->csam.s.fGatesChecked = csamInfo.fGatesChecked;
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync pVM->csam.s.fScanningStarted = csamInfo.fScanningStarted;
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync /* Restore dirty code page info. */
03319aeaef07ef63a404237f2cb56199131f4a03vboxsync memcpy(pVM->csam.s.pvDirtyBasePage, csamInfo.pvDirtyBasePage, sizeof(pVM->csam.s.pvDirtyBasePage));
5e797edc29f96c8367de4fbf5874171c24a89ba7vboxsync memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync /* Restore possible code page */
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync pVM->csam.s.cPossibleCodePages = csamInfo.cPossibleCodePages;
8e8844a522f5d335f177a0313b03067d79cce201vboxsync memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
9a12d720d4db6c26d09600ddab781ad8df5e1826vboxsync /* Restore pgdir bitmap (we'll change the pointers next). */
8e8844a522f5d335f177a0313b03067d79cce201vboxsync rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
03319aeaef07ef63a404237f2cb56199131f4a03vboxsync * Restore page bitmaps
79b24ef0ab7cd4a03a3571b3954c52ab8b573137vboxsync for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
e4ea543752422f1139923e3e506c625b0a1827c5vboxsync /* Convert to GC pointer. */
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync pVM->csam.s.pPDGCBitmapHC[i] = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC[i]);
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync /* Restore the bitmap. */
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
f827fea1108b8f8a1a5f63318f6ec3cf4a9e7010vboxsync * Restore page records
f9147fe1eaa4e35287f8f39282c7f92f0d7de0b7vboxsync for (uint32_t i=0;i<csamInfo.savedstate.cPageRecords + csamInfo.savedstate.cPatchPageRecords;i++)
fe49486f3c0fc3c327138e9a8f9ea9911d7c0d64vboxsync * Recreate the page record
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync rc = SSMR3GetMem(pSSM, pPage->pBitmap, CSAM_PAGE_BITMAP_SIZE);
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
e08de24d4792d31b7f2aac29db5cb8840d940009vboxsync * Convert guest context address to host context pointer
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * @returns VBox status code.
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * @param pVM The VM to operate on.
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * @param pCacheRec Address conversion cache record
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * @param pGCPtr Guest context pointer
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync * @returns Host context pointer or NULL in case of an error
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsyncstatic R3PTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
8e8844a522f5d335f177a0313b03067d79cce201vboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeAddrConv, a);
65b61798a61dd4c32cce448db1dac70bba8d5cf5vboxsync if (pCacheRec->pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
46df4404c8dbbf3672e7aae8cd0b2770356e5b73vboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync//// AssertMsgRC(rc, ("MMR3PhysGCVirt2HCVirtEx failed for %VRv\n", pGCPtr));
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync////invalid? Assert(sizeof(R3PTRTYPE(uint8_t*)) == sizeof(uint32_t));
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync pCacheRec->pPageLocStartHC = (R3PTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
e08de24d4792d31b7f2aac29db5cb8840d940009vboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * Read callback for disassembly function; supports reading bytes that cross a page boundary
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * @returns VBox status code.
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * @param pSrc GC source pointer
3dde2f85d4cf477621a3128887a2c08a8bca7c01vboxsync * @param pDest HC destination pointer
46df4404c8dbbf3672e7aae8cd0b2770356e5b73vboxsync * @param size Number of bytes to read
46df4404c8dbbf3672e7aae8cd0b2770356e5b73vboxsync * @param dwUserdata Callback specific user data (pCpu)
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsyncstatic DECLCALLBACK(int) CSAMR3ReadBytes(RTUINTPTR pSrc, uint8_t *pDest, unsigned size, void *pvUserdata)
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync RTHCUINTPTR pInstrHC = (RTHCUINTPTR)pCpu->apvUserData[1];
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync RTGCUINTPTR32 pInstrGC = (uintptr_t)pCpu->apvUserData[2];
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync /* We are not interested in patched instructions, so read the original opcode bytes. */
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync /** @note single instruction patches (int3) are checked in CSAMR3AnalyseCallback */
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync for (int i=0;i<orgsize;i++)
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync int rc = PATMR3QueryOpcode(pVM, (RTRCPTR)pSrc, pDest);
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pVM, pSrc))
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync return PGMPhysSimpleReadGCPtr(pVM, pDest, pSrc, size);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* pInstrHC is the base address; adjust according to the GC pointer. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncinline int CSAMR3DISInstr(PVM pVM, DISCPUSTATE *pCpu, RTRCPTR InstrGC, uint8_t *InstrHC, uint32_t *pOpsize, char *pszOutput)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync (pCpu)->apvUserData[2] = (void *)InstrGC; Assert(sizeof(InstrGC) <= sizeof(pCpu->apvUserData[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, OPTYPE_ALL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We are interested in everything except harmless stuff */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, ~(OPTYPE_INVALID | OPTYPE_HARMLESS | OPTYPE_RRM_MASK));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Analyses the instructions following the cli for compliance with our heuristics for cli
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCpu CPU disassembly state
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstrGC Guest context pointer to privileged instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCurInstrGC Guest context pointer to the current instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCacheRec GC to HC cache record
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pUserData User pointer (callback specific)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync //two byte int 3
e9a217d585085a6a6d129d27ca0d96a1b8e6d0eevboxsync /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* removing breaks win2k guests? */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // Check for exit points
e17bd6c32a8dd64f2d42838f9028216465e2caf0vboxsync /* It's not a good idea to patch pushf instructions:
e17bd6c32a8dd64f2d42838f9028216465e2caf0vboxsync * - increases the chance of conflicts (code jumping to the next instruction)
e17bd6c32a8dd64f2d42838f9028216465e2caf0vboxsync * - better to patch the cli
e17bd6c32a8dd64f2d42838f9028216465e2caf0vboxsync * - code that branches before the cli will likely hit an int 3
e17bd6c32a8dd64f2d42838f9028216465e2caf0vboxsync * - in general doesn't offer any benefits as we don't allow nested patch blocks (IF is always 1)
49748bb305bd71f672cd083af208f4bb08c5d6abvboxsync PATMR3AddHint(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
49748bb305bd71f672cd083af208f4bb08c5d6abvboxsync /* Make sure the instructions that follow the cli have not been encountered before. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync while (true)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC + opsize, &pPage) == true)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync PATMR3DetectConflict(pVM, pCurInstrGC, pCurInstrGC + opsize);
b9ca93dd1ad44cb8b27679dc5624be2f7b7f7af5vboxsync pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
8b984478b755f4d3091b977d9beac9fb7434279fvboxsync Log(("CSAMGCVirtToHCVirt failed for %VRv\n", pCurInstrGC));
51d4024e2984de499ecd878341898f71f55cf9e0vboxsync cpu.mode = (pPage->fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
8b984478b755f4d3091b977d9beac9fb7434279fvboxsync rc = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* no break */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: str!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: lsl!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: lar!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: sgdt!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: sldt!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: sidt!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: smsw!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: verw!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: verr!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: cpuid!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: push cs!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Privileged instruction at %VRv: iret!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PATMR3HasBeenPatched(pVM, pCurInstrGC) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PATMR3InstallPatch(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return VINF_SUCCESS; /* Look no further in this branch. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // return or jump/call through a jump table
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J)
3123bb2477edc752585e4bbd8e4cfedaf87997d1vboxsync Log(("Control Flow instruction at %VRv: jmp!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Control Flow instruction at %VRv: call!!\n", pCurInstrGC));
78a205e3fc6719d59e8c561b3d287d3a4f879852vboxsync * Wrapper for csamAnalyseCodeStream for call instructions.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
78a205e3fc6719d59e8c561b3d287d3a4f879852vboxsync * @param pInstrGC Guest context pointer to privileged instruction
78a205e3fc6719d59e8c561b3d287d3a4f879852vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fCode32 16 or 32 bits code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pUserData User pointer (callback specific)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int csamAnalyseCallCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Check if we've previously encountered the instruction after the ret. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* New address; let's take a look at it. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
3e6d3b0af632bdcd931b5149915c7b8be1a732cdvboxsync * Some generic requirements for recognizing an adjacent function:
47b5427d1a541bcd269bc625c35b19d849071edfvboxsync * - alignment fillers that consist of:
a11c569636fa6838bd423f4631a9660a5a84204bvboxsync * - lea genregX, [genregX (+ 0)]
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * - push ebp after the filler (can extend this later); aligned at at least a 4 byte boundary
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (int j=0;j<16;j++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMGCVirtToHCVirt failed for %VRv\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc2 = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (RT_SUCCESS(rc2)) Log(("CSAM Call Analysis: %s", szOutput));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc2 = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Disassembly failed at %VRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /// @todo fault in the page
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Page for current instruction %VRv is not present!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync //all is fine, let's continue
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync break; /* acceptable */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Must be similar to:
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * lea esi, [esi]
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * lea esi, [esi+0]
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Any register is allowed as long as source and destination are identical.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync || !(cpu.param2.flags & (USE_DISPLACEMENT8|USE_DISPLACEMENT16|USE_DISPLACEMENT32))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync || cpu.param1.base.reg_gen32 != cpu.param2.base.reg_gen32
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Analyse the function. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Found new function at %VRv\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
0f5d1b2abd9e82c7ee46f1327287c44856604bcbvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
0f5d1b2abd9e82c7ee46f1327287c44856604bcbvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Analyse the function. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Found new function at %VRv\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Mark it as scanned. */
96d8c2c77873591a51233b013fc1ec8bc774a096vboxsync csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync } /* for at most 16 instructions */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync ; /* MSVC complains otherwise */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync#define csamAnalyseCallCodeStream csamAnalyseCodeStream
96d8c2c77873591a51233b013fc1ec8bc774a096vboxsync * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstrGC Guest context pointer to privileged instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCurInstrGC Guest context pointer to the current instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fCode32 16 or 32 bits code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pUserData User pointer (callback specific)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync LogFlow(("csamAnalyseCodeStream: code at %VRv depth=%d\n", pCurInstrGC, pCacheRec->depth));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Limit the call depth. (rather arbitrary upper limit; too low and we won't detect certain
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * cpuid instructions in Linux kernels; too high and we waste too much time scanning code)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * (512 is necessary to detect cpuid instructions in Red Hat EL4; see defect 1355)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @note we are using a lot of stack here. couple of 100k when we go to the full depth (!)
ee5858e9d955811dae9e6b8ecbb336cc6d0e7197vboxsync LogFlow(("CSAM: maximum calldepth reached for %VRv\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* New address; let's take a look at it. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync LogFlow(("Code at %VRv has been scanned before\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMGCVirtToHCVirt failed for %VRv\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc2 = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (RT_SUCCESS(rc2)) Log(("CSAM Analysis: %s", szOutput));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc2 = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Disassembly failed at %VRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /// @todo fault in the page
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Page for current instruction %VRv is not present!!\n", pCurInstrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync //all is fine, let's continue
10e1bc06b2908a0af56d92ffdbadd25b36a5ef61vboxsync csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * If it's harmless, then don't bother checking it (the disasm tables had better be accurate!)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if ((cpu.pCurInstr->optype & ~OPTYPE_RRM_MASK) == OPTYPE_HARMLESS)
9b45880674da6f82ca27cc28b0272de3dd3cc7dfvboxsync AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Remember the address of the instruction following the ret in case the parent instruction was a call. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync && pCacheRec->pCallExitRec->cInstrAfterRet < CSAM_MAX_CALLEXIT_RET)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + opsize;
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync rc = pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if ( ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync || (cpu.pCurInstr->opcode == OP_CALL && cpu.param1.flags == USE_DISPLACEMENT32)) /* simple indirect call (call dword ptr [address]) */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* 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) */
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync PGMPhysSimpleReadGCPtr(pVM, &addr, (RTRCUINTPTR)cpu.param1.disp32, sizeof(addr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsync /* If the target address lies in a patch generated jump, then special action needs to be taken. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Same page? */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pCurInstrGC ))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Page for current instruction %VRv is not present!!\n", addr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* All is fine, let's continue. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* New branch target; let's take a look at it. */
afed5ab737f4aacfae3fe73776f40e989190a7cavboxsync pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
96d8c2c77873591a51233b013fc1ec8bc774a096vboxsync rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync {//unconditional jump; return to caller
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync } //if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync && (cpu.param1.flags & (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)) == (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc2 = PGMPhysGCPtr2HCPtr(pVM, pJumpTableGC, (PRTHCPTR)&pJumpTableHC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Same page? */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pJumpTableGC))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync addr = *(RTRCPTR *)(pJumpTableHC + cpu.param1.scale * i);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* New branch target; let's take a look at it. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync break; //done!
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Calculates the 64 bits hash value for the current page
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns hash value
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
96d8c2c77873591a51233b013fc1ec8bc774a096vboxsync * @param pInstr Page address
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncuint64_t csamR3CalcPageHash(PVM pVM, RTRCPTR pInstr)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysSimpleReadGCPtr(pVM, &val[0], pInstr, sizeof(val[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamR3CalcPageHash: page %VRv not present!!\n", pInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysSimpleReadGCPtr(pVM, &val[1], pInstr+1024, sizeof(val[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamR3CalcPageHash: page %VRv not present!!\n", pInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysSimpleReadGCPtr(pVM, &val[2], pInstr+2048, sizeof(val[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamR3CalcPageHash: page %VRv not present!!\n", pInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysSimpleReadGCPtr(pVM, &val[3], pInstr+3072, sizeof(val[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamR3CalcPageHash: page %VRv not present!!\n", pInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysSimpleReadGCPtr(pVM, &val[4], pInstr+4092, sizeof(val[0]));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamR3CalcPageHash: page %VRv not present!!\n", pInstr));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // don't want to get division by zero traps
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync hash = (uint64_t)val[0] * (uint64_t)val[1] / (uint64_t)val[2] + (val[3]%val[4]);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Notify CSAM of a page flush
10e1bc06b2908a0af56d92ffdbadd25b36a5ef61vboxsync * @returns VBox status code
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsync * @param pVM The VM to operate on.
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync * @param addr GC address of the page to flush
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fRemovePage Page removal flag
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsyncstatic int csamFlushPage(PVM pVM, RTRCPTR addr, bool fRemovePage)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Returned at a very early stage (no paging yet presumably). */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* User page -> not relevant for us. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (rc != VERR_PAGE_NOT_PRESENT && rc != VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsgFailed(("PGMR3GetPage %VRv failed with %Rrc\n", addr, rc));
c33db29e7b41467a35675031f5f5233839909083vboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)addr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
10e1bc06b2908a0af56d92ffdbadd25b36a5ef61vboxsync Log(("CSAMR3FlushPage: page %VRv has changed -> FLUSH (rc=%Rrc) (Phys: %VGp vs %VGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
9b45880674da6f82ca27cc28b0272de3dd3cc7dfvboxsync rc = PGMGstGetPage(pVM, addr, &pPageRec->page.fFlags, &pPageRec->page.GCPhys);
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync pPageRec->page.u64Hash = csamR3CalcPageHash(pVM, addr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, CSAM_PAGE_BITMAP_SIZE);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync memset(pPageRec->page.pBitmap, 0, CSAM_PAGE_BITMAP_SIZE);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Inform patch manager about the flush; no need to repeat the above check twice.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Notify CSAM of a page flush
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param addr GC address of the page to flush
223cf005b18af2c21352a70693ebaf0582f68ebcvboxsyncVMMR3DECL(int) CSAMR3FlushPage(PVM pVM, RTRCPTR addr)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return csamFlushPage(pVM, addr, true /* remove page record */);
8e8844a522f5d335f177a0313b03067d79cce201vboxsync * Remove a CSAM monitored page. Use with care!
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param addr GC address of the page to flush
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3RemovePage(PVM pVM, RTRCPTR addr)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)addr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Check a page record in case a page has been changed
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code. (trap handled or not)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstrGC GC instruction pointer
a27fbcbb29ffc2196c2ebd0f2dad92f40c7ec65dvboxsyncint csamR3CheckPageRecord(PVM pVM, RTRCPTR pInstrGC)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamFlushPage(pVM, pInstrGC, false /* don't remove page record */);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Returns monitor description based on CSAM tag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @return description string
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param enmTag Owner tag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncconst char *csamGetMonitorDescription(CSAMTAG enmTag)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return "CSAM-PATM self-modifying code monitor handler";
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return "CSAM-REM self-modifying code monitor handler";
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return "CSAM self-modifying code monitor handler";
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Adds page record to our lookup tree
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns CSAMPAGE ptr or NULL if failure
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtr Page address
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param enmTag Owner tag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fCode32 16 or 32 bits code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fMonitorInvalidation Monitor page invalidation flag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("New page record for %VRv\n", GCPtr & PAGE_BASE_GC_MASK));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage = (PCSAMPAGEREC)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, sizeof(CSAMPAGEREC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsgFailed(("csamCreatePageRecord: Out of memory!!!!\n"));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Round down to page boundary. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage->page.fMonitorInvalidation = fMonitorInvalidation;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, PAGE_SIZE/sizeof(uint8_t));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMGstGetPage(pVM, GCPtr, &pPage->page.fFlags, &pPage->page.GCPhys);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPage->page.u64Hash = csamR3CalcPageHash(pVM, GCPtr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync ret = RTAvlPVInsert(&pVM->csam.s.pPageTree, &pPage->Core);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync int rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VRv failed with %Rrc\n", GCPtr, rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("PGMR3HandlerVirtualRegisterEx for %VRv failed with %Rrc\n", GCPtr, rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Prefetch it in case it's not there yet. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwModifyPage(pVM, GCPtr, 1, 0, ~(uint64_t)X86_PTE_RW);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync break; /* to shut up GCC */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamCreatePageRecord %VRv HCPhys=%VGp\n", GCPtr, pPage->page.GCPhys));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync break; /* to shut up GCC */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Monitors a code page (if not already monitored)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pPageAddrGC The page to monitor
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param enmTag Monitor tag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3MonitorPage(PVM pVM, RTRCPTR pPageAddrGC, CSAMTAG enmTag)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Dirty pages must be handled before calling this function!. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3MonitorPage %VRv %d\n", pPageAddrGC, enmTag));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /** @todo implicit assumption */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMGstGetPage(pVM, pPageAddrGC, &fFlags, NULL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We don't care about user pages. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamCreatePageRecord(pVM, pPageAddrGC, enmTag, true /* 32 bits code */, fMonitorInvalidation);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /** @todo reference count */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3MonitorPage: activate monitoring for %VRv\n", pPageAddrGC));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VRv failed with %Rrc\n", pPageAddrGC, rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("PGMR3HandlerVirtualRegisterEx for %VRv failed with %Rrc\n", pPageAddrGC, rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Prefetch it in case it's not there yet. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec->page.fMonitorInvalidation = fMonitorInvalidation;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync PGMHandlerVirtualChangeInvalidateCallback(pVM, pPageRec->page.pPageGC, CSAMCodePageInvalidate);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Prefetch it in case it's not there yet. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Make sure it's readonly. Page invalidation may have modified the attributes. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync#if 0 /* def VBOX_STRICT -> very annoying) */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwGetPage(pVM, pPageAddrGC, &fPageShw, &GCPhys);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync// AssertMsg( (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync// || !(fPageShw & X86_PTE_RW)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %VRv (%RHp) aren't readonly (%VX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Prefetch it in case it's not there yet. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync#endif /* CSAM_MONITOR_CODE_PAGES */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Unmonitors a code page
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pPageAddrGC The page to monitor
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param enmTag Monitor tag
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3UnmonitorPage(PVM pVM, RTRCPTR pPageAddrGC, CSAMTAG enmTag)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3UnmonitorPage %VRv %d\n", pPageAddrGC, enmTag));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(pPageRec && pPageRec->page.enmTag == enmTag);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Removes a page record from our lookup tree
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtr Page address
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVRemove(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* @todo -> this is expensive (cr3 reload)!!!
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * if this happens often, then reuse it instead!!!
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Make sure the recompiler flushes its cache as this page is no longer monitored. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatPageRemoveREMFlush);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync CPUMSetChangedFlags(pVM, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync break; /* to shut up GCC */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (pPageRec->page.pBitmap) MMR3HeapFree(pPageRec->page.pBitmap);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Callback for delayed writes from non-EMT threads
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM VM Handle.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param cbBuf How much it's reading/writing.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(void) CSAMDelayedWriteHandler(PVM pVM, RTRCPTR GCPtr, size_t cbBuf)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * #PF Handler callback for virtual access handler ranges.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Important to realize that a physical page in a range can have aliases, and
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * for ALL and WRITE handlers these will also trigger.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VINF_SUCCESS if the handler have carried out the operation.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM VM Handle.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pvPtr The HC mapping of that address.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pvBuf What the guest is reading/writing.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param cbBuf How much it's reading/writing.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param enmAccessType The access type.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pvUser User argument.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
18db01bbe01d709ed64ef78717e98b94b7fee056vboxsync Log(("CSAMCodePageWriteHandler: write to %VGv size=%d\n", GCPtr, cbBuf));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Queue the write instead otherwise we'll get concurrency issues. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /** @note in theory not correct to let it write the data first before disabling a patch!
c20b837b2c912dd4dcaaa676e15acd2b230108f0vboxsync * (if it writes the same data as the patch jump and we replace it with obsolete opcodes)
c20b837b2c912dd4dcaaa676e15acd2b230108f0vboxsync Log(("CSAMCodePageWriteHandler: delayed write!\n"));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = VMR3ReqCallEx(pVM, VMREQDEST_ANY, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID,
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync (PFNRT)CSAMDelayedWriteHandler, 3, pVM, (RTRCPTR)GCPtr, cbBuf);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * #PF Handler callback for invalidation of virtual access handler ranges.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM VM Handle.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtr The virtual address the guest has changed.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /** @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. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamFlushPage(pVM, GCPtr, false /* don't remove page! */);
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync * Check if the current instruction has already been checked before
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync * @returns VBox status code. (trap handled or not)
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync * @param pVM The VM to operate on.
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync * @param pInstr Instruction pointer
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync * @param pPage CSAM patch structure pointer
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsyncbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage)
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeCheckAddr, a);
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync if ((*pPage)->pBitmap == NULL || ASMBitTest((*pPage)->pBitmap, offset))
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
69a0f42fa2c531eb2cffb6d6f482d828d9adab34vboxsync return true;
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync return false;
5dda7f07dab8a954e6c4cf2378b15f921e60d9aavboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (pPageRec->page.pBitmap == NULL || ASMBitTest(pPageRec->page.pBitmap, offset))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return true;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return false;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Mark an instruction in a page as scanned/not scanned
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pPage Patch structure pointer
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstr Instruction pointer
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param opsize Instruction size
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fScanned Mark as scanned or not
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t opsize, bool fScanned)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync LogFlow(("csamMarkCodeAsScanned %VRv opsize=%d\n", pInstr, opsize));
c657bea826c7455c93bd45eaebab63a4c7742c42vboxsync /** @todo should recreate empty bitmap if !fScanned */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // retn instructions can be scanned more than once
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (ASMBitTest(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK) == 0)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Scanned full page (%VRv) -> free bitmap\n", pInstr & PAGE_BASE_GC_MASK));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync ASMBitSet(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync ASMBitClear(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Mark an instruction in a page as scanned/not scanned
8a132edc1577cbe2a19cd778c1b2bea6ae5e8515vboxsync * @returns VBox status code.
8a132edc1577cbe2a19cd778c1b2bea6ae5e8515vboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstr Instruction pointer
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param opsize Instruction size
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param fScanned Mark as scanned or not
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3MarkCode(PVM pVM, RTRCPTR pInstr, uint32_t opsize, bool fScanned)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(!fScanned); /* other case not implemented. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (csamIsCodeScanned(pVM, pInstr, &pPage) == false)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(fScanned == true); /* other case should not be possible */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3MarkCode: %VRv size=%d fScanned=%d\n", pInstr, opsize, fScanned));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync csamMarkCode(pVM, pPage, pInstr, opsize, fScanned);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Scan and analyse code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCtxCore CPU context
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstrGC Instruction pointer
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3CheckCodeEx(PVM pVM, PCPUMCTXCORE pCtxCore, RTRCPTR pInstrGC)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pInstrGC) == true)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Assuming 32 bits code for now. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(SELMGetCpuModeFromSelector(pVM, pCtxCore->eflags, pCtxCore->cs, &pCtxCore->csHid) == CPUMODE_32BIT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pInstrGC = SELMToFlat(pVM, DIS_SELREG_CS, pCtxCore, pInstrGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Scan and analyse code
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pInstrGC Instruction pointer (0:32 virtual address)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3CheckCode(PVM pVM, RTRCPTR pInstrGC)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pInstrGC) == true)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync // Cache record for PATMGCVirtToHCVirt
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("csamAnalyseCodeStream failed with %d\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Flush dirty code pages
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatFlushDirtyPages, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Notify the recompiler that this page has been changed. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Enable write protection again. (use the fault address as it might be an alias) */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMShwModifyPage(pVM, pVM->csam.s.pvDirtyFaultPage[i], 1, 0, ~(uint64_t)X86_PTE_RW);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3FlushDirtyPages: flush %VRv (modifypage rc=%Rrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (pPageRec && pPageRec->page.enmTag == CSAM_TAG_REM)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We don't care about user pages. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatFlushDirtyPages, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Flush potential new code pages
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (uint32_t i=0;i<pVM->csam.s.cPossibleCodePages;i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Resync the page to make sure instruction fetch will fault */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Perform any pending actions
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Analyse interrupt and trap gates
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param iGate Start gate
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param cGates Number of gates to check
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3CheckGates(PVM pVM, uint32_t iGate, uint32_t cGates)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Enabling interrupt gates only works when raw ring 0 is enabled. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync //AssertFailed();
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We only check all gates once during a session */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* We only check all gates once during a session */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (unsigned i=0;i<RT_ELEMENTS(pVM->csam.s.pvCallInstruction);i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync RTRCPTR pHandler = pVM->csam.s.pvCallInstruction[i];
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync CSAMP2GLOOKUPREC cacheRec = {0}; /* Cache record for PATMGCVirtToHCVirt. */
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsync Log(("CSAMCheckGates: checking previous call instruction %VRv\n", pHandler));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
9b45880674da6f82ca27cc28b0272de3dd3cc7dfvboxsync Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Determine valid upper boundary. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatCheckGates, a);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Get IDT entries.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (PAGE_ADDRESS(GCPtrIDT) == PAGE_ADDRESS(GCPtrIDT+cGates*sizeof(VBOXIDTE)))
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Just convert the IDT address to a HC pointer. The whole IDT fits in one page. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrIDT, (PRTHCPTR)&pGuestIdte);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsgRC(rc, ("Failed to read IDTE! rc=%Rrc\n", rc));
8f7bc6ad2b7bbcb4b3b96248cd2478e45f2e3b88vboxsync /* Slow method when it crosses a page boundary. */
585f64d6f624f9e683321dabeb21b0eb2e6aa473vboxsync rc = PGMPhysSimpleReadGCPtr(pVM, aIDT, GCPtrIDT, cGates*sizeof(VBOXIDTE));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync AssertMsgRC(rc, ("Failed to read IDTE! rc=%Rrc\n", rc));
61fa69e2bc9fc9e7490feed1c020273f3ddb238dvboxsync for (/*iGate*/; iGate<iGateEnd; iGate++, pGuestIdte++)
ec588a4ac8429a8b6c744544818b3ce3b2c75690vboxsync Assert(TRPMR3GetGuestTrapHandler(pVM, iGate) == TRPM_INVALID_HANDLER);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync && (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync && (pGuestIdte->Gen.u2DPL == 3 || pGuestIdte->Gen.u2DPL == 0)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync CSAMP2GLOOKUPREC cacheRec = {0}; /* Cache record for PATMGCVirtToHCVirt. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pHandler = SELMToFlatBySel(pVM, pGuestIdte->Gen.u16SegSel, pHandler);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = SELMR3GetSelectorInfo(pVM, pGuestIdte->Gen.u16SegSel, &selInfo);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* Refuse to patch a handler whose idt cs selector isn't wide open. */
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync Log(("CSAMCheckGates: check gate %d failed due to rc %Rrc GCPtrBase=%VRv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %VRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %VRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* OpenBSD guest specific patch test. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync RTGCUINTPTR32 aOpenBsdPushCSOffset[3] = {0x03, /* OpenBSD 3.7 & 3.8 */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (unsigned i=0;i<RT_ELEMENTS(aOpenBsdPushCSOffset);i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = CPUMR3DisasmInstrCPU(pVM, pCtx, pHandler - aOpenBsdPushCSOffset[i], &cpu, NULL);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Installed OpenBSD interrupt handler prefix instruction (push cs) patch\n"));
9c425bdea5f0991df62922b1584b805a86f2f898vboxsync /* Trap gates and certain interrupt gates. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync uint32_t fPatchFlags = PATMFL_CODE32 | PATMFL_IDTHANDLER;
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync /* No error code. */
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("Installing %s gate handler for 0x%X at %VRv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = PATMR3InstallPatch(pVM, pHandler, fPatchFlags);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync if (RT_SUCCESS(rc) || rc == VERR_PATM_ALREADY_PATCHED)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync RTRCPTR pNewHandlerGC = PATMR3QueryPatchGCPtr(pVM, pHandler);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync rc = TRPMR3SetGuestTrapHandler(pVM, iGate, pNewHandlerGC);
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("TRPMR3SetGuestTrapHandler %d failed with %Rrc\n", iGate, rc));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync } /* for */
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * Record previous call instruction addresses
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status code.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param GCPtrCall Call address
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncVMMR3DECL(int) CSAMR3RecordCallAddress(PVM pVM, RTRCPTR GCPtrCall)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync for (unsigned i=0;i<RT_ELEMENTS(pVM->csam.s.pvCallInstruction);i++)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync Log(("CSAMR3RecordCallAddress %VRv\n", GCPtrCall));
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync pVM->csam.s.pvCallInstruction[pVM->csam.s.iCallInstruction++] = GCPtrCall;
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync if (pVM->csam.s.iCallInstruction >= RT_ELEMENTS(pVM->csam.s.pvCallInstruction))
e4ea543752422f1139923e3e506c625b0a1827c5vboxsync * Query CSAM state (enabled/disabled)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns 0 - disabled, 1 - enabled
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * @param pVM The VM to operate on.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * The '.csamoff' command.
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * @returns VBox status.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCmd Pointer to the command descriptor (as registered).
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCmdHlp Pointer to command helper functions.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pVM Pointer to the current VM (if any).
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param paArgs Pointer to (readonly) array of arguments.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param cArgs Number of arguments in the array.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Validate input.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "CSAM Scanning disabled\n");
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * The '.csamon' command.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @returns VBox status.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCmd Pointer to the command descriptor (as registered).
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param pCmdHlp Pointer to command helper functions.
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * @param pVM Pointer to the current VM (if any).
aaeb2e2f6ed5b164f1dec9a16a7adeb84f64cf31vboxsync * @param paArgs Pointer to (readonly) array of arguments.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * @param cArgs Number of arguments in the array.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync * Validate input.
d408b82da0773c7e8cd4b3a01cb8a065a2c73a2dvboxsync return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");