CSAM.cpp revision fe95fa9fc2a364e3d678307971cc900b89f5c94a
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * CSAM - Guest OS Code Scanning and Analysis Manager
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Copyright (C) 2006-2007 Oracle Corporation
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * available from http://www.virtualbox.org. This file is free software;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * General Public License (GPL) as published by the Free Software
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync/*******************************************************************************
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync* Header Files *
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync*******************************************************************************/
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/* Enabled by default */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/* Enable to monitor code pages for self-modifying code. */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/* Enable to monitor all scanned pages
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync#define CSAM_MONITOR_CSAM_CODE_PAGES */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/* Enable to scan beyond ret instructions.
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync#define CSAM_ANALYSE_BEYOND_RET */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/*******************************************************************************
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync* Internal Functions *
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync*******************************************************************************/
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t opsize, bool fScanned);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/** @todo Temporary for debugging. */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic bool fInCSAMCodePageInvalidate = false;
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/*******************************************************************************
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync* Global Variables *
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync*******************************************************************************/
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsyncstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync/** Command descriptors. */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
4a9af9a8062589b741444d717d2dd1ed22b0f583vboxsync { "csamon", 0, 0, NULL, 0, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync { "csamoff", 0, 0, NULL, 0, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * SSM descriptor table for the CSAM structure.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /** @todo there are more fields that can be ignored here. */
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync SSMFIELD_ENTRY_PAD_HC64( CSAM, Alignment0, sizeof(uint32_t)),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR( CSAM, pPDBitmapGC), /// @todo ignore this?
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR( CSAM, pPDHCBitmapGC), /// @todo ignore this?
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY( CSAM, savedstate.cPatchPageRecords),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyBasePage),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyFaultPage),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvPossibleCodePage),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvCallInstruction),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAM, StatNrFlushesSkipped),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAM, StatCodePageModified),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAM, StatPageRemoveREMFlush),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAM, StatScanNextFunction),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAM, StatScanNextFunctionFailed),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync/** Fake type to simplify g_aCsamPDBitmapArray construction. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsynctypedef struct
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * SSM descriptor table for the CSAM::pPDBitmapHC array.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_HCPTR_NI_ARRAY(CSAMPDBITMAPARRAY, a),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * SSM descriptor table for the CSAMPAGEREC structure.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGN_HCPTR( CSAMPAGEREC, Core.pLeft),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGN_HCPTR( CSAMPAGEREC, Core.pRight),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync SSMFIELD_ENTRY_IGNORE( CSAMPAGEREC, Core.uchHeight),
7b4ea63789001468ec3662bdfcd6432bf89095dfvboxsync SSMFIELD_ENTRY_HCPTR_NI( CSAMPAGEREC, page.pBitmap),
7b4ea63789001468ec3662bdfcd6432bf89095dfvboxsync SSMFIELD_ENTRY( CSAMPAGEREC, page.fMonitorInvalidation),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Initializes the CSAM.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Allocate bitmap for the page directory. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTRCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pVM->csam.s.pPDBitmapGC = MMHyperR3ToRC(pVM, pVM->csam.s.pPDGCBitmapHC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pVM->csam.s.pPDHCBitmapGC = MMHyperR3ToRC(pVM, pVM->csam.s.pPDBitmapHC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Register save and load state notifiers.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = SSMR3RegisterInternal(pVM, "CSAM", 0, CSAM_SSM_VERSION, sizeof(pVM->csam.s) + PAGE_SIZE*16,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync 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.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
09f4b412099acda62997fd82c8608075c453b3ebvboxsync STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Check CFGM option and enable/disable CSAM.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "CSAMEnabled", &fEnabled);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Debugger commands.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync static bool fRegisteredCmds = false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * (Re)initializes CSAM
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * Assert alignment and sizes.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync AssertRelease(sizeof(pVM->csam.s) <= sizeof(pVM->csam.padding));
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * Setup any fixed pointers and offsets.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync PVMCPU pVCpu = &pVM->aCpus[0]; /* raw mode implies 1 VPCU */
09f4b412099acda62997fd82c8608075c453b3ebvboxsync VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_CSAM_PENDING_ACTION);
09f4b412099acda62997fd82c8608075c453b3ebvboxsync /* not necessary */
09f4b412099acda62997fd82c8608075c453b3ebvboxsync memset(pVM->csam.s.pvDirtyBasePage, 0, sizeof(pVM->csam.s.pvDirtyBasePage));
09f4b412099acda62997fd82c8608075c453b3ebvboxsync memset(pVM->csam.s.pvDirtyFaultPage, 0, sizeof(pVM->csam.s.pvDirtyFaultPage));
09f4b412099acda62997fd82c8608075c453b3ebvboxsync memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
09f4b412099acda62997fd82c8608075c453b3ebvboxsync memset(pVM->csam.s.pvCallInstruction, 0, sizeof(pVM->csam.s.pvCallInstruction));
09f4b412099acda62997fd82c8608075c453b3ebvboxsync /** @note never mess with the pgdir bitmap here! */
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * Applies relocations to data and code managed by this
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * component. This function will be called at init and
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * whenever the VMM need to relocate itself inside the GC.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * The csam will update the addresses used by the switcher.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @param pVM The VM.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @param offDelta Relocation delta.
09f4b412099acda62997fd82c8608075c453b3ebvboxsyncVMMR3DECL(void) CSAMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
09f4b412099acda62997fd82c8608075c453b3ebvboxsync /* Adjust pgdir and page bitmap pointers. */
09f4b412099acda62997fd82c8608075c453b3ebvboxsync pVM->csam.s.pPDBitmapGC = MMHyperR3ToRC(pVM, pVM->csam.s.pPDGCBitmapHC);
09f4b412099acda62997fd82c8608075c453b3ebvboxsync pVM->csam.s.pPDHCBitmapGC = MMHyperR3ToRC(pVM, pVM->csam.s.pPDBitmapHC);
09f4b412099acda62997fd82c8608075c453b3ebvboxsync for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * Terminates the csam.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * Termination means cleaning up and freeing all resources,
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * the VM it self is at this point powered off or suspended.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @returns VBox status code.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @param pVM The VM to operate on.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync /* @todo triggers assertion in MMHyperFree */
09f4b412099acda62997fd82c8608075c453b3ebvboxsync for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * CSAM reset callback.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @returns VBox status code.
09f4b412099acda62997fd82c8608075c453b3ebvboxsync * @param pVM The VM which is reset.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Clear page bitmaps. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync ASMMemZero32(pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Remove all CSAM page records. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync while(true)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PCSAMPAGEREC pPageRec = (PCSAMPAGEREC)RTAvlPVGetBestFit(&pVM->csam.s.pPageTree, 0, true);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Callback function for RTAvlPVDoWithAll
dc959f60f6d3e0cba86f7da4d39aa475913a7e10vboxsync * Counts the number of records in the tree
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
dc959f60f6d3e0cba86f7da4d39aa475913a7e10vboxsync * @param pNode Current node
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcPatches Pointer to patch counter
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic DECLCALLBACK(int) CountRecord(PAVLPVNODECORE pNode, void *pcPatches)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync *(uint32_t *)pcPatches = *(uint32_t *)pcPatches + 1;
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Callback function for RTAvlPVDoWithAll
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Saves the state of the page record
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @returns VBox status code.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pNode Current node
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pVM1 VM Handle
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsyncstatic DECLCALLBACK(int) SavePageState(PAVLPVNODECORE pNode, void *pVM1)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Save the page record itself */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3PutMem(pSSM, page.page.pBitmap, CSAM_PAGE_BITMAP_SIZE);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Execute state save operation.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @returns VBox status code.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pVM VM Handle.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pSSM SSM operation handle.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsyncstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Count the number of page records in the tree (feeling lazy)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, CountRecord, &csamInfo.savedstate.cPageRecords);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Save CSAM structure
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3PutMem(pSSM, &csamInfo, sizeof(csamInfo));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Save pgdir bitmap */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Save the page bitmap. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Save page records
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, SavePageState, pVM);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Execute state load operation.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @returns VBox status code.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pVM VM Handle.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pSSM SSM operation handle.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param uVersion Data layout version.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param uPass The data pass.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsyncstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync AssertMsgFailed(("csamR3Load: Invalid version uVersion=%d!\n", uVersion));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Restore CSAM structure
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetMem(pSSM, &csamInfo, sizeof(csamInfo));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetStructEx(pSSM, &csamInfo, sizeof(csamInfo), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamFields[0], NULL);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pVM->csam.s.fGatesChecked = csamInfo.fGatesChecked;
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pVM->csam.s.fScanningStarted = csamInfo.fScanningStarted;
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Restore dirty code page info. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync memcpy(pVM->csam.s.pvDirtyBasePage, csamInfo.pvDirtyBasePage, sizeof(pVM->csam.s.pvDirtyBasePage));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Restore possible code page */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pVM->csam.s.cPossibleCodePages = csamInfo.cPossibleCodePages;
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Restore pgdir bitmap (we'll change the pointers next). */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetStructEx(pSSM, pVM->csam.s.pPDBitmapHC, sizeof(uint8_t *) * CSAM_PGDIRBMP_CHUNKS,
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamPDBitmapArray[0], NULL);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Restore page bitmaps
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Convert to GC pointer. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pVM->csam.s.pPDGCBitmapHC[i] = MMHyperR3ToRC(pVM, pVM->csam.s.pPDBitmapHC[i]);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Restore the bitmap. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Restore page records
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync for (uint32_t i=0;i<csamInfo.savedstate.cPageRecords + csamInfo.savedstate.cPatchPageRecords;i++)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetStructEx(pSSM, &page, sizeof(page), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamPageRecFields[0], NULL);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Recreate the page record
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = SSMR3GetMem(pSSM, pPage->pBitmap, CSAM_PAGE_BITMAP_SIZE);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Note: we don't restore aDangerousInstr; it will be recreated automatically. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * Convert guest context address to host context pointer
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @returns VBox status code.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pVM The VM to operate on.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pCacheRec Address conversion cache record
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @param pGCPtr Guest context pointer
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @returns Host context pointer or NULL in case of an error
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsyncstatic R3PTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeAddrConv, a);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync if (pCacheRec->pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* Release previous lock if any. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync PGMPhysReleasePageMappingLock(pVM, &pCacheRec->Lock);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = PGMPhysGCPtr2CCPtrReadOnly(pVCpu, pGCPtr, (const void **)&pHCPtr, &pCacheRec->Lock);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync//// AssertMsgRC(rc, ("MMR3PhysGCVirt2HCVirtEx failed for %RRv\n", pGCPtr));
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync pCacheRec->pPageLocStartHC = (R3PTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync * @callback_method_impl{FNDISREADBYTES}
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsyncstatic DECLCALLBACK(int) CSAMR3ReadBytes(PDISCPUSTATE pDisState, uint8_t *pbDst, RTUINTPTR uSrcAddr, uint32_t cbToRead)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync RTHCUINTPTR pInstrHC = (RTHCUINTPTR)pDisState->pvUser2;
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync /* We are not interested in patched instructions, so read the original opcode bytes.
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync Note! single instruction patches (int3) are checked in CSAMR3AnalyseCallback */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync for (int i = 0; i < orgsize; i++)
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync int rc = PATMR3QueryOpcode(pVM, (RTRCPTR)uSrcAddr, pbDst);
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(uSrcAddr + cbToRead - 1) && !PATMIsPatchGCAddr(pVM, uSrcAddr))
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync return PGMPhysSimpleReadGCPtr(pVCpu, pbDst, uSrcAddr, cbToRead);
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync /* pInstrHC is the base address; adjust according to the GC pointer. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncDECLINLINE(int) CSAMR3DISInstr(PVM pVM, RTRCPTR InstrGC, uint8_t *InstrHC, DISCPUMODE enmCpuMode,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PDISCPUSTATE pCpu, uint32_t *pcbInstr, char *pszOutput, size_t cbOutput)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DISInstrToStrEx(InstrGC, enmCpuMode, CSAMR3ReadBytes, pVM, DISOPTYPE_ALL,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We are interested in everything except harmless stuff */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DISInstrToStrEx(InstrGC, enmCpuMode, CSAMR3ReadBytes, pVM, ~(DISOPTYPE_INVALID | DISOPTYPE_HARMLESS | DISOPTYPE_RRM_MASK),
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DISInstEx(InstrGC, enmCpuMode, ~(DISOPTYPE_INVALID | DISOPTYPE_HARMLESS | DISOPTYPE_RRM_MASK), CSAMR3ReadBytes, pVM,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Analyses the instructions following the cli for compliance with our heuristics for cli
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCpu CPU disassembly state
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstrGC Guest context pointer to privileged instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCurInstrGC Guest context pointer to the current instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCacheRec GC to HC cache record
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pUserData User pointer (callback specific)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync //two byte int 3
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* removing breaks win2k guests? */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync // Check for exit points
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* It's not a good idea to patch pushf instructions:
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - increases the chance of conflicts (code jumping to the next instruction)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - better to patch the cli
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - code that branches before the cli will likely hit an int 3
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - in general doesn't offer any benefits as we don't allow nested patch blocks (IF is always 1)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PATMR3AddHint(pVM, pCurInstrGC, (fCode32) ? PATMFL_CODE32 : 0);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Make sure the instructions that follow the cli have not been encountered before. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync while (true)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC + cbCurInstr, &pPage) == true)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PATMR3DetectConflict(pVM, pCurInstrGC, pCurInstrGC + cbCurInstr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync { /* Force pCurInstrHC out of scope after we stop using it (page lock!) */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMGCVirtToHCVirt failed for %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = CSAMR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, (fCode32) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* no break */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: str!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: lsl!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: lar!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: sgdt!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: sldt!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: sidt!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: smsw!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: verw!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: verr!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: cpuid!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: push cs!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Privileged instruction at %RRv: iret!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (PATMR3HasBeenPatched(pVM, pCurInstrGC) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PATMR3InstallPatch(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return VINF_SUCCESS; /* Look no further in this branch. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync // return or jump/call through a jump table
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Control Flow instruction at %RRv: jmp!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Control Flow instruction at %RRv: call!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Wrapper for csamAnalyseCodeStream for call instructions.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync * @param pInstrGC Guest context pointer to privileged instruction
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync * @param pCurInstrGC Guest context pointer to the current instruction
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync * @param fCode32 16 or 32 bits code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pUserData User pointer (callback specific)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int csamAnalyseCallCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Check if we've previously encountered the instruction after the ret. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* New address; let's take a look at it. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Some generic requirements for recognizing an adjacent function:
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - alignment fillers that consist of:
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - lea genregX, [genregX (+ 0)]
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * - push ebp after the filler (can extend this later); aligned at at least a 4 byte boundary
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for (int j = 0; j < 16; j++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync uint8_t *pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMGCVirtToHCVirt failed for %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc2 = CSAMR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, (fCode32) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (RT_SUCCESS(rc2)) Log(("CSAM Call Analysis: %s", szOutput));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc2 = CSAMR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, (fCode32) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, cbInstr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + cbInstr - 1))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (!PGMGstIsPagePresent(pVM, pCurInstrGC + cbInstr - 1))
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync /// @todo fault in the page
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Page for current instruction %RRv is not present!!\n", pCurInstrGC));
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync //all is fine, let's continue
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamR3CheckPageRecord(pVM, pCurInstrGC + cbInstr - 1);
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync break; /* acceptable */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Must be similar to:
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync * lea esi, [esi]
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync * lea esi, [esi+0]
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Any register is allowed as long as source and destination are identical.
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync || !(cpu.param2.flags & (DISUSE_DISPLACEMENT8|DISUSE_DISPLACEMENT16|DISUSE_DISPLACEMENT32))
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync || cpu.param1.base.reg_gen32 != cpu.param2.base.reg_gen32
09f4b412099acda62997fd82c8608075c453b3ebvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Analyse the function. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Found new function at %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Analyse the function. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Found new function at %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Mark it as scanned. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamMarkCode(pVM, pPage, pCurInstrGC, cbInstr, true);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync } /* for at most 16 instructions */
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync ; /* MSVC complains otherwise */
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync#define csamAnalyseCallCodeStream csamAnalyseCodeStream
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstrGC Guest context pointer to privileged instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCurInstrGC Guest context pointer to the current instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param fCode32 16 or 32 bits code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pUserData User pointer (callback specific)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync LogFlow(("csamAnalyseCodeStream: code at %RRv depth=%d\n", pCurInstrGC, pCacheRec->depth));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Limit the call depth. (rather arbitrary upper limit; too low and we won't detect certain
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * cpuid instructions in Linux kernels; too high and we waste too much time scanning code)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * (512 is necessary to detect cpuid instructions in Red Hat EL4; see defect 1355)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @note we are using a lot of stack here. couple of 100k when we go to the full depth (!)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync LogFlow(("CSAM: maximum calldepth reached for %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* New address; let's take a look at it. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync LogFlow(("Code at %RRv has been scanned before\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync { /* Force pCurInstrHC out of scope after we stop using it (page lock!) */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync uint8_t *pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMGCVirtToHCVirt failed for %RRv\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc2 = CSAMR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, fCode32 ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (RT_SUCCESS(rc2)) Log(("CSAM Analysis: %s", szOutput));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc2 = CSAMR3DISInstr(pVM, pCurInstrGC, pCurInstrHC, fCode32 ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, cbInstr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamMarkCode(pVM, pPage, pCurInstrGC, cbInstr, true);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + cbInstr - 1))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (!PGMGstIsPagePresent(pVCpu, pCurInstrGC + cbInstr - 1))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /// @todo fault in the page
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Page for current instruction %RRv is not present!!\n", pCurInstrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync //all is fine, let's continue
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamR3CheckPageRecord(pVM, pCurInstrGC + cbInstr - 1);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * If it's harmless, then don't bother checking it (the disasm tables had better be accurate!)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if ((cpu.pCurInstr->optype & ~DISOPTYPE_RRM_MASK) == DISOPTYPE_HARMLESS)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
e6ad2e18e663b076aeabfec994947514566a7accvboxsync /* Remember the address of the instruction following the ret in case the parent instruction was a call. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync && pCacheRec->pCallExitRec->cInstrAfterRet < CSAM_MAX_CALLEXIT_RET)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + cbInstr;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync // For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if ( ((cpu.pCurInstr->optype & DISOPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync || (cpu.pCurInstr->uOpcode == OP_CALL && cpu.param1.fUse == DISUSE_DISPLACEMENT32)) /* simple indirect call (call dword ptr [address]) */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* 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) */
dc959f60f6d3e0cba86f7da4d39aa475913a7e10vboxsync PGMPhysSimpleReadGCPtr(pVCpu, &addr, (RTRCUINTPTR)cpu.param1.uDisp.i32, sizeof(addr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.fUse));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* If the target address lies in a patch generated jump, then special action needs to be taken. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Same page? */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pCurInstrGC ))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Page for current instruction %RRv is not present!!\n", addr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* All is fine, let's continue. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* New branch target; let's take a look at it. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync {//unconditional jump; return to caller
e6ad2e18e663b076aeabfec994947514566a7accvboxsync } //if ((cpu.pCurInstr->optype & DISOPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync && (cpu.param1.fUse & (DISUSE_DISPLACEMENT32|DISUSE_INDEX|DISUSE_SCALE)) == (DISUSE_DISPLACEMENT32|DISUSE_INDEX|DISUSE_SCALE)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc2 = PGMPhysGCPtr2CCPtrReadOnly(pVCpu, pJumpTableGC, (PRTHCPTR)&pJumpTableHC, missing page lock);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Same page? */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pJumpTableGC))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync addr = *(RTRCPTR *)(pJumpTableHC + cpu.param1.scale * i);
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync /* New branch target; let's take a look at it. */
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync break; //done!
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Calculates the 64 bits hash value for the current page
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns hash value
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstr Page address
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncuint64_t csamR3CalcPageHash(PVM pVM, RTRCPTR pInstr)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, &val[0], pInstr, sizeof(val[0]));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("csamR3CalcPageHash: page %RRv not present!!\n", pInstr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, &val[1], pInstr+1024, sizeof(val[0]));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
e6ad2e18e663b076aeabfec994947514566a7accvboxsync Log(("csamR3CalcPageHash: page %RRv not present!!\n", pInstr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, &val[2], pInstr+2048, sizeof(val[0]));
e6ad2e18e663b076aeabfec994947514566a7accvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("csamR3CalcPageHash: page %RRv not present!!\n", pInstr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, &val[3], pInstr+3072, sizeof(val[0]));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("csamR3CalcPageHash: page %RRv not present!!\n", pInstr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, &val[4], pInstr+4092, sizeof(val[0]));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("csamR3CalcPageHash: page %RRv not present!!\n", pInstr));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync // don't want to get division by zero traps
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync hash = (uint64_t)val[0] * (uint64_t)val[1] / (uint64_t)val[2] + (val[3]%val[4]);
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync * Notify CSAM of a page flush
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code
e6ad2e18e663b076aeabfec994947514566a7accvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param addr GC address of the page to flush
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param fRemovePage Page removal flag
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int csamFlushPage(PVM pVM, RTRCPTR addr, bool fRemovePage)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Returned at a very early stage (no paging yet presumably). */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* User page -> not relevant for us. */
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
5793d23a719d4902824a3649b6fef3822ddd5fc7vboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (rc != VERR_PAGE_NOT_PRESENT && rc != VERR_PAGE_TABLE_NOT_PRESENT)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsgFailed(("PGMR3GetPage %RRv failed with %Rrc\n", addr, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)addr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3FlushPage: page %RRv has changed -> FLUSH (rc=%Rrc) (Phys: %RGp vs %RGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMGstGetPage(pVCpu, addr, &pPageRec->page.fFlags, &pPageRec->page.GCPhys);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec->page.u64Hash = csamR3CalcPageHash(pVM, addr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, CSAM_PAGE_BITMAP_SIZE);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync memset(pPageRec->page.pBitmap, 0, CSAM_PAGE_BITMAP_SIZE);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Inform patch manager about the flush; no need to repeat the above check twice.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Notify CSAM of a page flush
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param addr GC address of the page to flush
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3FlushPage(PVM pVM, RTRCPTR addr)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return csamFlushPage(pVM, addr, true /* remove page record */);
eb2d4958f7faf812c3bdb2d7587d815022f0bd55vboxsync * Remove a CSAM monitored page. Use with care!
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param addr GC address of the page to flush
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3RemovePage(PVM pVM, RTRCPTR addr)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)addr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Check a page record in case a page has been changed
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code. (trap handled or not)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstrGC GC instruction pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncint csamR3CheckPageRecord(PVM pVM, RTRCPTR pInstrGC)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)pInstrGC);
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsync csamFlushPage(pVM, pInstrGC, false /* don't remove page record */);
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsync * Returns monitor description based on CSAM tag
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsync * @return description string
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsync * @param enmTag Owner tag
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsyncconst char *csamGetMonitorDescription(CSAMTAG enmTag)
441b60f8b0601cc1718368c9c3ef082223ad12a2vboxsync return "CSAM-PATM self-modifying code monitor handler";
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return "CSAM-REM self-modifying code monitor handler";
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return "CSAM self-modifying code monitor handler";
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Adds page record to our lookup tree
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns CSAMPAGE ptr or NULL if failure
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param GCPtr Page address
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param enmTag Owner tag
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param fCode32 16 or 32 bits code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param fMonitorInvalidation Monitor page invalidation flag
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("New page record for %RRv\n", GCPtr & PAGE_BASE_GC_MASK));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPage = (PCSAMPAGEREC)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, sizeof(CSAMPAGEREC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsgFailed(("csamCreatePageRecord: Out of memory!!!!\n"));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Round down to page boundary. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPage->page.fMonitorInvalidation = fMonitorInvalidation;
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync pPage->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, PAGE_SIZE/sizeof(uint8_t));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMGstGetPage(pVCpu, GCPtr, &pPage->page.fFlags, &pPage->page.GCPhys);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPage->page.u64Hash = csamR3CalcPageHash(pVM, GCPtr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync ret = RTAvlPVInsert(&pVM->csam.s.pPageTree, &pPage->Core);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", GCPtr, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("PGMR3HandlerVirtualRegisterEx for %RRv failed with %Rrc\n", GCPtr, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Prefetch it in case it's not there yet. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMShwMakePageReadonly(pVCpu, GCPtr, 0 /*fFlags*/);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync break; /* to shut up GCC */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Log(("csamCreatePageRecord %RRv GCPhys=%RGp\n", GCPtr, pPage->page.GCPhys));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync break; /* to shut up GCC */
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync * Monitors a code page (if not already monitored)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync * @param pPageAddrGC The page to monitor
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param enmTag Monitor tag
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3MonitorPage(PVM pVM, RTRCPTR pPageAddrGC, CSAMTAG enmTag)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Dirty pages must be handled before calling this function!. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3MonitorPage %RRv %d\n", pPageAddrGC, enmTag));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /** @todo implicit assumption */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)pPageAddrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMGstGetPage(pVCpu, pPageAddrGC, &fFlags, NULL);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We don't care about user pages. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamCreatePageRecord(pVM, pPageAddrGC, enmTag, true /* 32 bits code */, fMonitorInvalidation);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)pPageAddrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /** @todo reference count */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3MonitorPage: activate monitoring for %RRv\n", pPageAddrGC));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", pPageAddrGC, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("PGMR3HandlerVirtualRegisterEx for %RRv failed with %Rrc\n", pPageAddrGC, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Prefetch it in case it's not there yet. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMShwMakePageReadonly(pVCpu, pPageAddrGC, 0 /*fFlags*/);
4c98b8b05f3783351cf256cc90cd4478fb28b62bvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec->page.fMonitorInvalidation = fMonitorInvalidation;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PGMHandlerVirtualChangeInvalidateCallback(pVM, pPageRec->page.pPageGC, CSAMCodePageInvalidate);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Prefetch it in case it's not there yet. */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync /* Make sure it's readonly. Page invalidation may have modified the attributes. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMShwMakePageReadonly(pVCpu, pPageAddrGC, 0 /*fFlags*/);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync#if 0 /* def VBOX_STRICT -> very annoying) */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync rc = PGMShwGetPage(pVCpu, pPageAddrGC, &fPageShw, &GCPhys);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync// AssertMsg( (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync// || !(fPageShw & X86_PTE_RW)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %RRv (%RHp) aren't readonly (%RX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync /* Prefetch it in case it's not there yet. */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync /* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync rc = PGMShwMakePageReadonly(pVCpu, pPageAddrGC, 0 /*fFlags*/);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync#endif /* CSAM_MONITOR_CODE_PAGES */
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * Unmonitors a code page
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @returns VBox status code
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pPageAddrGC The page to monitor
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param enmTag Monitor tag
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3UnmonitorPage(PVM pVM, RTRCPTR pPageAddrGC, CSAMTAG enmTag)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3UnmonitorPage %RRv %d\n", pPageAddrGC, enmTag));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)pPageAddrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(pPageRec && pPageRec->page.enmTag == enmTag);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Removes a page record from our lookup tree
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param GCPtr Page address
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVRemove(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)GCPtr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* @todo -> this is expensive (cr3 reload)!!!
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * if this happens often, then reuse it instead!!!
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync /* Make sure the recompiler flushes its cache as this page is no longer monitored. */
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync STAM_COUNTER_INC(&pVM->csam.s.StatPageRemoveREMFlush);
105b1a31b6037dbe14acb8d09e60da540885202bvboxsync CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync break; /* to shut up GCC */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pPageRec->page.pBitmap) MMR3HeapFree(pPageRec->page.pBitmap);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * Callback for delayed writes from non-EMT threads
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pVM VM Handle.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cbBuf How much it's reading/writing.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsyncstatic DECLCALLBACK(void) CSAMDelayedWriteHandler(PVM pVM, RTRCPTR GCPtr, size_t cbBuf)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync int rc = PATMR3PatchWrite(pVM, GCPtr, (uint32_t)cbBuf);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * \#PF Handler callback for virtual access handler ranges.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * Important to realize that a physical page in a range can have aliases, and
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * for ALL and WRITE handlers these will also trigger.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @returns VINF_SUCCESS if the handler have carried out the operation.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pVM VM Handle.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pvPtr The HC mapping of that address.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pvBuf What the guest is reading/writing.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param cbBuf How much it's reading/writing.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param enmAccessType The access type.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync * @param pvUser User argument.
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsyncstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType);
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Log(("CSAMCodePageWriteHandler: write to %RGv size=%zu\n", GCPtr, cbBuf));
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync if ( PAGE_ADDRESS(pvPtr) == PAGE_ADDRESS((uintptr_t)pvPtr + cbBuf - 1)
9cdd4d805ecb43126372f7cf12e4032836cb738avboxsync Log(("CSAMCodePageWriteHandler: dummy write -> ignore\n"));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PATMR3PatchWrite(pVM, GCPtr, (uint32_t)cbBuf);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Queue the write instead otherwise we'll get concurrency issues. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /** @note in theory not correct to let it write the data first before disabling a patch!
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * (if it writes the same data as the patch jump and we replace it with obsolete opcodes)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCodePageWriteHandler: delayed write!\n"));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = VMR3ReqCallVoidNoWait(pVM, VMCPUID_ANY, (PFNRT)CSAMDelayedWriteHandler, 3, pVM, (RTRCPTR)GCPtr, cbBuf);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * \#PF Handler callback for invalidation of virtual access handler ranges.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM VM Handle.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param GCPtr The virtual address the guest has changed.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /** @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. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamFlushPage(pVM, GCPtr, false /* don't remove page! */);
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync * Check if the current instruction has already been checked before
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code. (trap handled or not)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstr Instruction pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pPage CSAM patch structure pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatTimeCheckAddr, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if ((*pPage)->pBitmap == NULL || ASMBitTest((*pPage)->pBitmap, offset))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return true;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)pInstr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pPageRec->page.pBitmap == NULL || ASMBitTest(pPageRec->page.pBitmap, offset))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return true;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Mark an instruction in a page as scanned/not scanned
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pPage Patch structure pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstr Instruction pointer
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync * @param cbInstr Instruction size
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync * @param fScanned Mark as scanned or not
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsyncstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t cbInstr, bool fScanned)
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync LogFlow(("csamMarkCodeAsScanned %RRv cbInstr=%d\n", pInstr, cbInstr));
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync /** @todo should recreate empty bitmap if !fScanned */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync // retn instructions can be scanned more than once
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (ASMBitTest(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK) == 0)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Scanned full page (%RRv) -> free bitmap\n", pInstr & PAGE_BASE_GC_MASK));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync ASMBitSet(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync ASMBitClear(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Mark an instruction in a page as scanned/not scanned
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstr Instruction pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cbInstr Instruction size
521d8df5a1a304406b911c2e2c7bf9214d6d9200vboxsync * @param fScanned Mark as scanned or not
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3MarkCode(PVM pVM, RTRCPTR pInstr, uint32_t cbInstr, bool fScanned)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(!fScanned); /* other case not implemented. */
521d8df5a1a304406b911c2e2c7bf9214d6d9200vboxsync if (csamIsCodeScanned(pVM, pInstr, &pPage) == false)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(fScanned == true); /* other case should not be possible */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3MarkCode: %RRv size=%d fScanned=%d\n", pInstr, cbInstr, fScanned));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync csamMarkCode(pVM, pPage, pInstr, cbInstr, fScanned);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Scan and analyse code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCtxCore CPU context
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstrGC Instruction pointer
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3CheckCodeEx(PVM pVM, PCPUMCTXCORE pCtxCore, RTRCPTR pInstrGC)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pInstrGC) == true)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Assuming 32 bits code for now. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(SELMGetCpuModeFromSelector(VMMGetCpu0(pVM), pCtxCore->eflags, pCtxCore->cs, &pCtxCore->csHid) == DISCPUMODE_32BIT);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pInstrGC = SELMToFlat(pVM, DISSELREG_CS, pCtxCore, pInstrGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Scan and analyse code
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pInstrGC Instruction pointer (0:32 virtual address)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3CheckCode(PVM pVM, RTRCPTR pInstrGC)
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync /* Not active. */
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync /* Cache record for CSAMGCVirtToHCVirt */
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
0cb5af53de7aed97b1b35311f93de8175756e4afvboxsync PGMPhysReleasePageMappingLock(pVM, &cacheRec.Lock);
0cb5af53de7aed97b1b35311f93de8175756e4afvboxsync Log(("csamAnalyseCodeStream failed with %d\n", rc));
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync * Flush dirty code pages
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync * @returns VBox status code.
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync * @param pVM The VM to operate on.
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync STAM_PROFILE_START(&pVM->csam.s.StatFlushDirtyPages, a);
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync /* Notify the recompiler that this page has been changed. */
6d9d4a12f6f9f341c968f711765c2c17c9a5a28cvboxsync /* Enable write protection again. (use the fault address as it might be an alias) */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMShwMakePageReadonly(pVCpu, pVM->csam.s.pvDirtyFaultPage[i], 0 /*fFlags*/);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3FlushDirtyPages: flush %RRv (modifypage rc=%Rrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)(uintptr_t)GCPtr);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pPageRec && pPageRec->page.enmTag == CSAM_TAG_REM)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We don't care about user pages. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_STOP(&pVM->csam.s.StatFlushDirtyPages, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Flush potential new code pages
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for (uint32_t i=0;i<pVM->csam.s.cPossibleCodePages;i++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PGMShwMakePageNotPresent(pVCpu, GCPtr, 0 /*fFlags*/);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Resync the page to make sure instruction fetch will fault */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Perform any pending actions
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVCpu The VMCPU to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3DoPendingAction(PVM pVM, PVMCPU pVCpu)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_CSAM_PENDING_ACTION);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Analyse interrupt and trap gates
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param iGate Start gate
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cGates Number of gates to check
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3CheckGates(PVM pVM, uint32_t iGate, uint32_t cGates)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync RTRCPTR GCPtrIDT = CPUMGetGuestIDTR(pVCpu, &cbIDT);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Enabling interrupt gates only works when raw ring 0 is enabled. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync //AssertFailed();
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We only check all gates once during a session */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* We only check all gates once during a session */
d64aa881df34a86dd70ab0e1b7b94ffa41be9731vboxsync for (unsigned i=0;i<RT_ELEMENTS(pVM->csam.s.pvCallInstruction);i++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync RTRCPTR pHandler = pVM->csam.s.pvCallInstruction[i];
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync CSAMP2GLOOKUPREC cacheRec; /* Cache record for CSAMGCVirtToHCVirt. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: checking previous call instruction %RRv\n", pHandler));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PGMPhysReleasePageMappingLock(pVM, &cacheRec.Lock);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Determine valid upper boundary. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync STAM_PROFILE_START(&pVM->csam.s.StatCheckGates, a);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Get IDT entries.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PGMPhysSimpleReadGCPtr(pVCpu, aIDT, GCPtrIDT, cGates*sizeof(VBOXIDTE));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync AssertMsgRC(rc, ("Failed to read IDTE! rc=%Rrc\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for (/*iGate*/; iGate<iGateEnd; iGate++, pGuestIdte++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Assert(TRPMR3GetGuestTrapHandler(pVM, iGate) == TRPM_INVALID_HANDLER);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync && (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync && (pGuestIdte->Gen.u2DPL == 3 || pGuestIdte->Gen.u2DPL == 0)
d64aa881df34a86dd70ab0e1b7b94ffa41be9731vboxsync CSAMP2GLOOKUPREC cacheRec; /* Cache record for CSAMGCVirtToHCVirt. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pHandler = SELMToFlatBySel(pVM, pGuestIdte->Gen.u16SegSel, pHandler);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = SELMR3GetSelectorInfo(pVM, pVCpu, pGuestIdte->Gen.u16SegSel, &selInfo);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync || (selInfo.fFlags & (DBGFSELINFO_FLAGS_NOT_PRESENT | DBGFSELINFO_FLAGS_INVALID))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Refuse to patch a handler whose idt cs selector isn't wide open. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: check gate %d failed due to rc %Rrc GCPtrBase=%RRv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync PGMPhysReleasePageMappingLock(pVM, &cacheRec.Lock);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* OpenBSD guest specific patch test. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync RTGCUINTPTR32 aOpenBsdPushCSOffset[3] = {0x03, /* OpenBSD 3.7 & 3.8 */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for (unsigned i=0;i<RT_ELEMENTS(aOpenBsdPushCSOffset);i++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = CPUMR3DisasmInstrCPU(pVM, pVCpu, pCtx, pHandler - aOpenBsdPushCSOffset[i], &cpu, NULL);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Installed OpenBSD interrupt handler prefix instruction (push cs) patch\n"));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Trap gates and certain interrupt gates. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync uint32_t fPatchFlags = PATMFL_CODE32 | PATMFL_IDTHANDLER;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* No error code. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("Installing %s gate handler for 0x%X at %RRv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = PATMR3InstallPatch(pVM, pHandler, fPatchFlags);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (RT_SUCCESS(rc) || rc == VERR_PATM_ALREADY_PATCHED)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync RTRCPTR pNewHandlerGC = PATMR3QueryPatchGCPtr(pVM, pHandler);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = TRPMR3SetGuestTrapHandler(pVM, iGate, pNewHandlerGC);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("TRPMR3SetGuestTrapHandler %d failed with %Rrc\n", iGate, rc));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync } /* for */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Record previous call instruction addresses
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param GCPtrCall Call address
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncVMMR3DECL(int) CSAMR3RecordCallAddress(PVM pVM, RTRCPTR GCPtrCall)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync for (unsigned i=0;i<RT_ELEMENTS(pVM->csam.s.pvCallInstruction);i++)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync Log(("CSAMR3RecordCallAddress %RRv\n", GCPtrCall));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync pVM->csam.s.pvCallInstruction[pVM->csam.s.iCallInstruction++] = GCPtrCall;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (pVM->csam.s.iCallInstruction >= RT_ELEMENTS(pVM->csam.s.pvCallInstruction))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Query CSAM state (enabled/disabled)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns 0 - disabled, 1 - enabled
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM The VM to operate on.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * The '.csamoff' command.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCmd Pointer to the command descriptor (as registered).
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCmdHlp Pointer to command helper functions.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM Pointer to the current VM (if any).
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param paArgs Pointer to (readonly) array of arguments.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cArgs Number of arguments in the array.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "CSAMDisableScanning");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DBGCCmdHlpPrintf(pCmdHlp, "CSAM Scanning disabled\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * The '.csamon' command.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns VBox status.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCmd Pointer to the command descriptor (as registered).
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pCmdHlp Pointer to command helper functions.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pVM Pointer to the current VM (if any).
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param paArgs Pointer to (readonly) array of arguments.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cArgs Number of arguments in the array.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "CSAMEnableScanning");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return DBGCCmdHlpPrintf(pCmdHlp, "CSAM Scanning enabled\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync#endif /* VBOX_WITH_DEBUGGER */