1
/* $Id: CSAMAll.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
3
* CSAM - Guest OS Code Scanning and Analysis Manager - Any Context
7
* Copyright (C) 2006-2007 innotek GmbH
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License as published by the Free Software Foundation,
13
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14
* distribution. VirtualBox OSE is distributed in the hope that it will
15
* be useful, but WITHOUT ANY WARRANTY of any kind.
19
/*******************************************************************************
21
*******************************************************************************/
22
#define LOG_GROUP LOG_GROUP_CSAM
23
#include <VBox/cpum.h>
24
#include <VBox/stam.h>
25
#include <VBox/patm.h>
26
#include <VBox/csam.h>
31
#include <VBox/param.h>
33
#include "CSAMInternal.h"
38
#include <iprt/assert.h>
40
#include <VBox/disopcode.h>
41
#include <iprt/string.h>
45
* Check if this page needs to be analysed by CSAM
47
* @returns VBox status code
48
* @param pVM The VM to operate on.
49
* @param pvFault Fault address
51
CSAMDECL(int) CSAMExecFault(PVM pVM, RTGCPTR pvFault)
53
if(!CSAMIsEnabled(pVM))
56
LogFlow(("CSAMGCExecFault: for page %08X scanned=%d\n", pvFault, CSAMIsPageScanned(pVM, pvFault)));
58
if(CSAMIsPageScanned(pVM, pvFault))
61
STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesGC, 1);
65
STAM_COUNTER_ADD(&pVM->csam.s.StatNrTraps, 1);
66
VM_FF_SET(pVM, VM_FF_CSAM_SCAN_PAGE);
67
return VINF_CSAM_PENDING_ACTION;
72
* Check if this page was previously scanned by CSAM
74
* @returns true -> scanned, false -> not scanned
75
* @param pVM The VM to operate on.
76
* @param pPage GC page address
78
CSAMDECL(bool) CSAMIsPageScanned(PVM pVM, RTGCPTR pPage)
83
page = (uintptr_t)pPage;
84
pgdir = page >> X86_PAGE_4M_SHIFT;
85
bit = (page & X86_PAGE_4M_OFFSET_MASK) >> X86_PAGE_4K_SHIFT;
87
Assert(pgdir < CSAM_PGDIRBMP_CHUNKS);
88
Assert(bit < PAGE_SIZE);
90
return pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir] && ASMBitTest(pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
96
* Mark a page as scanned/not scanned
98
* @note: we always mark it as scanned, even if we haven't completely done so
100
* @returns VBox status code.
101
* @param pVM The VM to operate on.
102
* @param pPage GC page address (not necessarily aligned)
103
* @param fScanned Mark as scanned or not scanned
106
CSAMDECL(int) CSAMMarkPage(PVM pVM, RTGCPTR pPage, bool fScanned)
112
if (fScanned && !CSAMIsPageScanned(pVM, pPage))
113
Log(("CSAMMarkPage %VGv\n", pPage));
116
if(!CSAMIsEnabled(pVM))
119
page = (uintptr_t)pPage;
120
pgdir = page >> X86_PAGE_4M_SHIFT;
121
bit = (page & X86_PAGE_4M_OFFSET_MASK) >> X86_PAGE_4K_SHIFT;
123
Assert(pgdir < CSAM_PGDIRBMP_CHUNKS);
124
Assert(bit < PAGE_SIZE);
126
if(!CTXSUFF(pVM->csam.s.pPDBitmap)[pgdir])
128
STAM_COUNTER_INC(&pVM->csam.s.StatBitmapAlloc);
129
int rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir]);
130
if (VBOX_FAILURE(rc))
132
Log(("MMR3HyperAlloc failed with %d\n", rc));
136
pVM->csam.s.pPDHCBitmapGC[pgdir] = MMHyperGC2HC(pVM, pVM->csam.s.pPDBitmapGC[pgdir]);
137
if (!pVM->csam.s.pPDHCBitmapGC[pgdir])
139
Log(("MMHyperHC2GC failed for %VGv\n", pVM->csam.s.pPDBitmapGC[pgdir]));
143
pVM->csam.s.pPDGCBitmapHC[pgdir] = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC[pgdir]);
144
if (!pVM->csam.s.pPDGCBitmapHC[pgdir])
146
Log(("MMHyperHC2GC failed for %VHv\n", pVM->csam.s.pPDBitmapHC[pgdir]));
152
ASMBitSet(pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
154
ASMBitClear(pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
160
* Check if this page needs to be analysed by CSAM.
162
* This function should only be called for supervisor pages and
163
* only when CSAM is enabled. Leaving these selection criteria
164
* to the caller simplifies the interface (PTE passing).
166
* Note the the page has not yet been synced, so the TLB trick
167
* (which wasn't ever active anyway) cannot be applied.
169
* @returns true if the page should be marked not present because
170
* CSAM want need to scan it.
171
* @returns false if the page was already scanned.
172
* @param pVM The VM to operate on.
173
* @param GCPtr GC pointer of page
175
CSAMDECL(bool) CSAMDoesPageNeedScanning(PVM pVM, RTGCPTR GCPtr)
177
if(!CSAMIsEnabled(pVM))
180
if(CSAMIsPageScanned(pVM, GCPtr))
182
/* Already checked! */
183
STAM_COUNTER_ADD(&CTXSUFF(pVM->csam.s.StatNrKnownPages), 1);
186
STAM_COUNTER_ADD(&CTXSUFF(pVM->csam.s.StatNrPageNP), 1);
192
* Remember a possible code page for later inspection
194
* @returns VBox status code.
195
* @param pVM The VM to operate on.
196
* @param GCPtr GC pointer of page
198
CSAMDECL(void) CSAMMarkPossibleCodePage(PVM pVM, RTGCPTR GCPtr)
200
if (pVM->csam.s.cPossibleCodePages < RT_ELEMENTS(pVM->csam.s.pvPossibleCodePage))
202
pVM->csam.s.pvPossibleCodePage[pVM->csam.s.cPossibleCodePages++] = GCPtr;
203
VM_FF_SET(pVM, VM_FF_CSAM_PENDING_ACTION);
210
* Turn on code scanning
212
* @returns VBox status code.
213
* @param pVM The VM to operate on.
215
CSAMDECL(int) CSAMEnableScanning(PVM pVM)
217
pVM->fCSAMEnabled = true;
222
* Turn off code scanning
224
* @returns VBox status code.
225
* @param pVM The VM to operate on.
227
CSAMDECL(int) CSAMDisableScanning(PVM pVM)
229
pVM->fCSAMEnabled = false;
235
* Check if we've scanned this instruction before. If true, then we can emulate
236
* it instead of returning to ring 3.
238
* Using a simple array here as there are generally few mov crx instructions and
239
* tree lookup is likely to be more expensive. (as it would also have to be offset based)
242
* @param pVM The VM to operate on.
243
* @param GCPtr GC pointer of page table entry
245
CSAMDECL(bool) CSAMIsKnownDangerousInstr(PVM pVM, RTGCPTR GCPtr)
247
for (uint32_t i=0;i<pVM->csam.s.cDangerousInstr;i++)
249
if (pVM->csam.s.aDangerousInstr[i] == GCPtr)
251
STAM_COUNTER_INC(&pVM->csam.s.StatInstrCacheHit);
255
/* Record that we're about to process it in ring 3. */
256
pVM->csam.s.aDangerousInstr[pVM->csam.s.iDangerousInstr++] = GCPtr;
257
pVM->csam.s.iDangerousInstr &= CSAM_MAX_DANGR_INSTR_MASK;
259
if (++pVM->csam.s.cDangerousInstr > CSAM_MAX_DANGR_INSTR)
260
pVM->csam.s.cDangerousInstr = CSAM_MAX_DANGR_INSTR;
262
STAM_COUNTER_INC(&pVM->csam.s.StatInstrCacheMiss);