VirtualBox

source: vbox/trunk/src/VBox/VMM/PATM/CSAM.cpp@ 3840

Last change on this file since 3840 was 3020, checked in by vboxsync, 17 years ago

Added missing space after ')' in macro invocations so VCC doesn't mess up the precompiler output.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 87.2 KB
Line 
1/* $Id: CSAM.cpp 3020 2007-06-04 12:01:53Z vboxsync $ */
2/** @file
3 * CSAM - Guest OS Code Scanning and Analysis Manager
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
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.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_CSAM
26#include <VBox/cpum.h>
27#include <VBox/stam.h>
28#include <VBox/patm.h>
29#include <VBox/csam.h>
30#include <VBox/cpumdis.h>
31#include <VBox/pgm.h>
32#include <VBox/iom.h>
33#include <VBox/sup.h>
34#include <VBox/mm.h>
35#include <VBox/em.h>
36#include <VBox/rem.h>
37#include <VBox/selm.h>
38#include <VBox/trpm.h>
39#include <VBox/cfgm.h>
40#include <VBox/param.h>
41#include <iprt/avl.h>
42#include <iprt/asm.h>
43#include <iprt/thread.h>
44#include "CSAMInternal.h"
45#include <VBox/vm.h>
46#include <VBox/dbg.h>
47#include <VBox/err.h>
48#include <VBox/ssm.h>
49#include <VBox/log.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52#include <VBox/dis.h>
53#include <VBox/disopcode.h>
54#include <stdlib.h>
55#include <stdio.h>
56
57
58/* Enabled by default */
59#define CSAM_ENABLE
60
61/* Enable to monitor code pages for self-modifying code. */
62#define CSAM_MONITOR_CODE_PAGES
63/* Enable to monitor all scanned pages
64#define CSAM_MONITOR_CSAM_CODE_PAGES */
65/* Enable to scan beyond ret instructions.
66#define CSAM_ANALYSE_BEYOND_RET */
67
68/*******************************************************************************
69* Internal Functions *
70*******************************************************************************/
71static DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
72static DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
73static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
74static DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
75
76bool csamIsCodeScanned(PVM pVM, RTGCPTR pInstr, PCSAMPAGE *pPage);
77int csamR3CheckPageRecord(PVM pVM, RTGCPTR pInstr);
78static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTGCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
79static int csamRemovePageRecord(PVM pVM, RTGCPTR GCPtr);
80static int csamReinit(PVM pVM);
81static void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTGCPTR pInstr, uint32_t opsize, bool fScanned);
82static int csamAnalyseCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
83 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
84
85/** @todo Temporary for debugging. */
86static bool fInCSAMCodePageInvalidate = false;
87
88/*******************************************************************************
89* Global Variables *
90*******************************************************************************/
91#ifdef VBOX_WITH_DEBUGGER
92static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
93static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
94
95/** Command descriptors. */
96static const DBGCCMD g_aCmds[] =
97{
98 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
99 { "csamon", 0, 0, NULL, 0, NULL, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
100 { "csamoff", 0, 0, NULL, 0, NULL, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
101};
102#endif
103
104
105/**
106 * Initializes the CSAM.
107 *
108 * @returns VBox status code.
109 * @param pVM The VM to operate on.
110 */
111CSAMR3DECL(int) CSAMR3Init(PVM pVM)
112{
113 int rc;
114
115 LogFlow(("CSAMR3Init\n"));
116
117 /* Allocate bitmap for the page directory. */
118 rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
119 AssertRCReturn(rc, rc);
120 rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTGCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
121 AssertRCReturn(rc, rc);
122 pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
123 pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
124
125 rc = csamReinit(pVM);
126 AssertRCReturn(rc, rc);
127
128 /*
129 * Register save and load state notificators.
130 */
131 rc = SSMR3RegisterInternal(pVM, "CSAM", 0, CSAM_SSM_VERSION, sizeof(pVM->csam.s) + PAGE_SIZE*16,
132 NULL, csamr3Save, NULL,
133 NULL, csamr3Load, NULL);
134 AssertRCReturn(rc, rc);
135
136 STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
137 STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
138
139 STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
140 STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
141 STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
142 STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
143 STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
144 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.");
145
146 STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
147 STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
148 STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
149 STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
150 STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
151 STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
152
153 STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
154
155 STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
156 STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
157 STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
158 STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
159 STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
160 STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
161 STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
162
163 STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
164
165 STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
166 STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
167
168 STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
169 STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
170
171 STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
172 STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
173 STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
174 STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
175 STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
176 STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
177 STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
178
179 /*
180 * Check CFGM option and enable/disable CSAM.
181 */
182 bool fEnabled;
183 rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "CSAMEnabled", &fEnabled);
184 if (VBOX_FAILURE(rc))
185#ifdef CSAM_ENABLE
186 fEnabled = true;
187#else
188 fEnabled = false;
189#endif
190 if (fEnabled)
191 CSAMEnableScanning(pVM);
192
193#ifdef VBOX_WITH_DEBUGGER
194 /*
195 * Debugger commands.
196 */
197 static bool fRegisteredCmds = false;
198 if (!fRegisteredCmds)
199 {
200 int rc = DBGCRegisterCommands(&g_aCmds[0], ELEMENTS(g_aCmds));
201 if (VBOX_SUCCESS(rc))
202 fRegisteredCmds = true;
203 }
204#endif
205
206 return VINF_SUCCESS;
207}
208
209/**
210 * (Re)initializes CSAM
211 *
212 * @param pVM The VM.
213 */
214static int csamReinit(PVM pVM)
215{
216 /*
217 * Assert alignment and sizes.
218 */
219 AssertRelease(!(RT_OFFSETOF(VM, csam.s) & 31));
220 AssertRelease(sizeof(pVM->csam.s) <= sizeof(pVM->csam.padding));
221
222 /*
223 * Setup any fixed pointers and offsets.
224 */
225 pVM->csam.s.offVM = RT_OFFSETOF(VM, patm);
226
227 pVM->csam.s.fGatesChecked = false;
228 pVM->csam.s.fScanningStarted = false;
229
230 VM_FF_CLEAR(pVM, VM_FF_CSAM_PENDING_ACTION);
231 pVM->csam.s.cDirtyPages = 0;
232 /* not necessary */
233 memset(pVM->csam.s.pvDirtyBasePage, 0, sizeof(pVM->csam.s.pvDirtyBasePage));
234 memset(pVM->csam.s.pvDirtyFaultPage, 0, sizeof(pVM->csam.s.pvDirtyFaultPage));
235
236 memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
237 pVM->csam.s.cDangerousInstr = 0;
238 pVM->csam.s.iDangerousInstr = 0;
239
240 /** @note never mess with the pgdir bitmap here! */
241 return VINF_SUCCESS;
242}
243
244/**
245 * Applies relocations to data and code managed by this
246 * component. This function will be called at init and
247 * whenever the VMM need to relocate itself inside the GC.
248 *
249 * The csam will update the addresses used by the switcher.
250 *
251 * @param pVM The VM.
252 * @param offDelta Relocation delta.
253 */
254CSAMR3DECL(void) CSAMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
255{
256 if (offDelta)
257 {
258 /* Adjust pgdir and page bitmap pointers. */
259 pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
260 pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
261
262 for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
263 {
264 if (pVM->csam.s.pPDGCBitmapHC[i])
265 {
266 pVM->csam.s.pPDGCBitmapHC[i] += offDelta;
267 }
268 }
269 }
270 return;
271}
272
273/**
274 * Terminates the csam.
275 *
276 * Termination means cleaning up and freeing all resources,
277 * the VM it self is at this point powered off or suspended.
278 *
279 * @returns VBox status code.
280 * @param pVM The VM to operate on.
281 */
282CSAMR3DECL(int) CSAMR3Term(PVM pVM)
283{
284 int rc;
285
286 rc = CSAMR3Reset(pVM);
287 AssertRC(rc);
288
289 /* @todo triggers assertion in MMHyperFree */
290#if 0
291 for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
292 {
293 if (pVM->csam.s.pPDBitmapHC[i])
294 MMHyperFree(pVM, pVM->csam.s.pPDBitmapHC[i]);
295 }
296#endif
297
298 return VINF_SUCCESS;
299}
300
301/**
302 * CSAM reset callback.
303 *
304 * @returns VBox status code.
305 * @param pVM The VM which is reset.
306 */
307CSAMR3DECL(int) CSAMR3Reset(PVM pVM)
308{
309 /* Clear page bitmaps. */
310 for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
311 {
312 if (pVM->csam.s.pPDBitmapHC[i])
313 {
314 Assert((CSAM_PAGE_BITMAP_SIZE& 3) == 0);
315 ASMMemZero32(pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
316 }
317 }
318
319 /* Remove all CSAM page records. */
320 while(true)
321 {
322 PCSAMPAGEREC pPageRec = (PCSAMPAGEREC)RTAvlPVGetBestFit(&pVM->csam.s.pPageTree, 0, true);
323 if (pPageRec)
324 {
325 csamRemovePageRecord(pVM, pPageRec->page.pPageGC);
326 }
327 else
328 break;
329 }
330 Assert(!pVM->csam.s.pPageTree);
331
332 csamReinit(pVM);
333
334 return VINF_SUCCESS;
335}
336
337#define CSAM_SUBTRACT_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) - (uintptr_t)(b)
338#define CSAM_ADD_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) + (uintptr_t)(b)
339
340
341/**
342 * Callback function for RTAvlPVDoWithAll
343 *
344 * Counts the number of records in the tree
345 *
346 * @returns VBox status code.
347 * @param pNode Current node
348 * @param pcPatches Pointer to patch counter
349 */
350static DECLCALLBACK(int) CountRecord(PAVLPVNODECORE pNode, void *pcPatches)
351{
352 *(uint32_t *)pcPatches = *(uint32_t *)pcPatches + 1;
353 return VINF_SUCCESS;
354}
355
356/**
357 * Callback function for RTAvlPVDoWithAll
358 *
359 * Saves the state of the page record
360 *
361 * @returns VBox status code.
362 * @param pNode Current node
363 * @param pVM1 VM Handle
364 */
365static DECLCALLBACK(int) SavePageState(PAVLPVNODECORE pNode, void *pVM1)
366{
367 PVM pVM = (PVM)pVM1;
368 PCSAMPAGEREC pPage = (PCSAMPAGEREC)pNode;
369 CSAMPAGEREC page = *pPage;
370 PSSMHANDLE pSSM = pVM->csam.s.savedstate.pSSM;
371 int rc;
372
373 /* Save the page record itself */
374 rc = SSMR3PutMem(pSSM, &page, sizeof(page));
375 AssertRCReturn(rc, rc);
376
377 if (page.page.pBitmap)
378 {
379 rc = SSMR3PutMem(pSSM, page.page.pBitmap, CSAM_PAGE_BITMAP_SIZE);
380 AssertRCReturn(rc, rc);
381 }
382
383 return VINF_SUCCESS;
384}
385
386/**
387 * Execute state save operation.
388 *
389 * @returns VBox status code.
390 * @param pVM VM Handle.
391 * @param pSSM SSM operation handle.
392 */
393static DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM)
394{
395 CSAM csamInfo = pVM->csam.s;
396 int rc;
397
398 /*
399 * Count the number of page records in the tree (feeling lazy)
400 */
401 csamInfo.savedstate.cPageRecords = 0;
402 RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, CountRecord, &csamInfo.savedstate.cPageRecords);
403
404 /*
405 * Save CSAM structure
406 */
407 pVM->csam.s.savedstate.pSSM = pSSM;
408 rc = SSMR3PutMem(pSSM, &csamInfo, sizeof(csamInfo));
409 AssertRCReturn(rc, rc);
410
411 /* Save pgdir bitmap */
412 rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
413 AssertRCReturn(rc, rc);
414
415 for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
416 {
417 if(csamInfo.pPDBitmapHC[i])
418 {
419 /* Save the page bitmap. */
420 rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
421 AssertRCReturn(rc, rc);
422 }
423 }
424
425 /*
426 * Save page records
427 */
428 rc = RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, SavePageState, pVM);
429 AssertRCReturn(rc, rc);
430
431 /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
432 return VINF_SUCCESS;
433}
434
435/**
436 * Execute state load operation.
437 *
438 * @returns VBox status code.
439 * @param pVM VM Handle.
440 * @param pSSM SSM operation handle.
441 * @param u32Version Data layout version.
442 */
443static DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
444{
445 int rc;
446 CSAM csamInfo;
447
448 if (u32Version != CSAM_SSM_VERSION)
449 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
450
451 pVM->csam.s.savedstate.pSSM = pSSM;
452
453 /*
454 * Restore CSAM structure
455 */
456 rc = SSMR3GetMem(pSSM, &csamInfo, sizeof(csamInfo));
457 AssertRCReturn(rc, rc);
458
459 pVM->csam.s.fGatesChecked = csamInfo.fGatesChecked;
460 pVM->csam.s.fScanningStarted = csamInfo.fScanningStarted;
461
462 /* Restore dirty code page info. */
463 pVM->csam.s.cDirtyPages = csamInfo.cDirtyPages;
464 memcpy(pVM->csam.s.pvDirtyBasePage, csamInfo.pvDirtyBasePage, sizeof(pVM->csam.s.pvDirtyBasePage));
465 memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
466
467 /* Restore possible code page */
468 pVM->csam.s.cPossibleCodePages = csamInfo.cPossibleCodePages;
469 memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
470
471 /* Restore pgdir bitmap (we'll change the pointers next). */
472 rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
473 AssertRCReturn(rc, rc);
474
475 /*
476 * Restore page bitmaps
477 */
478 for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
479 {
480 if(pVM->csam.s.pPDBitmapHC[i])
481 {
482 rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
483 if (VBOX_FAILURE(rc))
484 {
485 Log(("MMR3HyperAlloc failed with %d\n", rc));
486 return rc;
487 }
488 /* Convert to GC pointer. */
489 pVM->csam.s.pPDGCBitmapHC[i] = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC[i]);
490 Assert(pVM->csam.s.pPDGCBitmapHC[i]);
491
492 /* Restore the bitmap. */
493 rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
494 AssertRCReturn(rc, rc);
495 }
496 else
497 {
498 Assert(!pVM->csam.s.pPDGCBitmapHC[i]);
499 pVM->csam.s.pPDGCBitmapHC[i] = 0;
500 }
501 }
502
503 /*
504 * Restore page records
505 */
506 for (uint32_t i=0;i<csamInfo.savedstate.cPageRecords + csamInfo.savedstate.cPatchPageRecords;i++)
507 {
508 CSAMPAGEREC page;
509 PCSAMPAGE pPage;
510
511 rc = SSMR3GetMem(pSSM, &page, sizeof(page));
512 AssertRCReturn(rc, rc);
513
514 /*
515 * Recreate the page record
516 */
517 pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
518 AssertReturn(pPage, VERR_NO_MEMORY);
519
520 pPage->GCPhys = page.page.GCPhys;
521 pPage->fFlags = page.page.fFlags;
522 pPage->u64Hash = page.page.u64Hash;
523
524 if (page.page.pBitmap)
525 {
526 rc = SSMR3GetMem(pSSM, pPage->pBitmap, CSAM_PAGE_BITMAP_SIZE);
527 AssertRCReturn(rc, rc);
528 }
529 else
530 {
531 MMR3HeapFree(pPage->pBitmap);
532 pPage->pBitmap = 0;
533 }
534 }
535
536 /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
537 memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
538 pVM->csam.s.cDangerousInstr = 0;
539 pVM->csam.s.iDangerousInstr = 0;
540 return VINF_SUCCESS;
541}
542
543/**
544 * Convert guest context address to host context pointer
545 *
546 * @returns VBox status code.
547 * @param pVM The VM to operate on.
548 * @param pCacheRec Address conversion cache record
549 * @param pGCPtr Guest context pointer
550 *
551 * @returns Host context pointer or NULL in case of an error
552 *
553 */
554static HCPTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, GCPTRTYPE(uint8_t *) pGCPtr)
555{
556 int rc;
557 HCPTRTYPE(void *) pHCPtr;
558
559 STAM_PROFILE_START(&pVM->csam.s.StatTimeAddrConv, a);
560
561 pHCPtr = PATMR3GCPtrToHCPtr(pVM, pGCPtr);
562 if (pHCPtr) return pHCPtr;
563
564 if (pCacheRec->pPageLocStartHC)
565 {
566 uint32_t offset = pGCPtr & PAGE_OFFSET_MASK;
567 if (pCacheRec->pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
568 {
569 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
570 return pCacheRec->pPageLocStartHC + offset;
571 }
572 }
573
574 rc = PGMPhysGCPtr2HCPtr(pVM, pGCPtr, &pHCPtr);
575 if (rc != VINF_SUCCESS)
576 {
577//// AssertMsgRC(rc, ("MMR3PhysGCVirt2HCVirtEx failed for %VGv\n", pGCPtr));
578 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
579 return NULL;
580 }
581////invalid? Assert(sizeof(HCPTRTYPE(uint8_t*)) == sizeof(uint32_t));
582
583 pCacheRec->pPageLocStartHC = (HCPTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
584 pCacheRec->pGuestLoc = pGCPtr & PAGE_BASE_GC_MASK;
585 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
586 return pHCPtr;
587}
588
589/**
590 * Read callback for disassembly function; supports reading bytes that cross a page boundary
591 *
592 * @returns VBox status code.
593 * @param pSrc GC source pointer
594 * @param pDest HC destination pointer
595 * @param size Number of bytes to read
596 * @param dwUserdata Callback specific user data (pCpu)
597 *
598 */
599int32_t CSAMR3ReadBytes(RTHCUINTPTR pSrc, uint8_t *pDest, uint32_t size, RTHCUINTPTR dwUserdata)
600{
601 DISCPUSTATE *pCpu = (DISCPUSTATE *)dwUserdata;
602 PVM pVM = (PVM)pCpu->dwUserData[0];
603 RTHCUINTPTR pInstrHC = pCpu->dwUserData[1];
604 RTGCUINTPTR pInstrGC = pCpu->dwUserData[2];
605 int orgsize = size;
606
607 Assert(sizeof(RTHCUINTPTR) <= sizeof(pCpu->dwUserData[0]));
608 Assert(sizeof(RTGCUINTPTR) <= sizeof(pCpu->dwUserData[0]));
609
610 /* We are not interested in patched instructions, so read the original opcode bytes. */
611 /** @note single instruction patches (int3) are checked in CSAMR3AnalyseCallback */
612 for (int i=0;i<orgsize;i++)
613 {
614 int rc = PATMR3QueryOpcode(pVM, (RTGCPTR)pSrc, pDest);
615 if (VBOX_SUCCESS(rc))
616 {
617 pSrc++;
618 pDest++;
619 size--;
620 }
621 else
622 break;
623 }
624 if (size == 0)
625 return VINF_SUCCESS;
626
627 if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pVM, pSrc))
628 {
629 return PGMPhysReadGCPtr(pVM, pDest, pSrc, size);
630 }
631 else
632 {
633 Assert(pInstrHC);
634
635 /* pInstrHC is the base address; adjust according to the GC pointer. */
636 pInstrHC = pInstrHC + (pSrc - pInstrGC);
637
638 memcpy(pDest, (void *)pInstrHC, size);
639 }
640
641 return VINF_SUCCESS;
642}
643
644inline bool CSAMR3DISInstr(PVM pVM, DISCPUSTATE *pCpu, RTGCPTR InstrGC, uint8_t *InstrHC, uint32_t *pOpsize, char *pszOutput)
645{
646 (pCpu)->pfnReadBytes = CSAMR3ReadBytes;
647 (pCpu)->dwUserData[0] = (RTHCUINTPTR)pVM;
648 (pCpu)->dwUserData[1] = (RTHCUINTPTR)InstrHC;
649 (pCpu)->dwUserData[2] = (RTHCUINTPTR)InstrGC;
650#ifdef DEBUG
651 return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, OPTYPE_ALL);
652#else
653 /* We are interested in everything except harmless stuff */
654 return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, ~(OPTYPE_INVALID | OPTYPE_HARMLESS | OPTYPE_RRM_MASK));
655#endif
656}
657
658/**
659 * Analyses the instructions following the cli for compliance with our heuristics for cli
660 *
661 * @returns VBox status code.
662 * @param pVM The VM to operate on.
663 * @param pCpu CPU disassembly state
664 * @param pInstrGC Guest context pointer to privileged instruction
665 * @param pCurInstrGC Guest context pointer to the current instruction
666 * @param pCacheRec GC to HC cache record
667 * @param pUserData User pointer (callback specific)
668 *
669 */
670static int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC,
671 PCSAMP2GLOOKUPREC pCacheRec, void *pUserData)
672{
673 PCSAMPAGE pPage = (PCSAMPAGE)pUserData;
674 int rc;
675
676 switch(pCpu->pCurInstr->opcode)
677 {
678 case OP_INT:
679 Assert(pCpu->param1.flags & USE_IMMEDIATE8);
680 if (pCpu->param1.parval == 3)
681 {
682 //two byte int 3
683 return VINF_SUCCESS;
684 }
685 break;
686
687 case OP_ILLUD2:
688 /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue. */
689 case OP_RETN:
690 case OP_INT3:
691 case OP_INVALID:
692#if 1
693 /* removing breaks win2k guests? */
694 case OP_IRET:
695#endif
696 return VINF_SUCCESS;
697 }
698
699 // Check for exit points
700 switch (pCpu->pCurInstr->opcode)
701 {
702 /* It's not a good idea to patch pushf instructions:
703 * - increases the chance of conflicts (code jumping to the next instruction)
704 * - better to patch the cli
705 * - code that branches before the cli will likely hit an int 3
706 * - in general doesn't offer any benefits as we don't allow nested patch blocks (IF is always 1)
707 */
708 case OP_PUSHF:
709 case OP_POPF:
710 break;
711
712 case OP_CLI:
713 {
714 uint32_t cbInstr = 0;
715 uint32_t opsize = pCpu->opsize;
716
717 PATMR3AddHint(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
718
719 /* Make sure the instructions that follow the cli have not been encountered before. */
720 while (true)
721 {
722 DISCPUSTATE cpu;
723 uint8_t *pCurInstrHC = 0;
724 bool disret;
725
726 if (cbInstr + opsize >= SIZEOF_NEARJUMP32)
727 break;
728
729 if (csamIsCodeScanned(pVM, pCurInstrGC + opsize, &pPage) == true)
730 {
731 /* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
732 PATMR3DetectConflict(pVM, pCurInstrGC, pCurInstrGC + opsize);
733 break;
734 }
735 pCurInstrGC += opsize;
736 cbInstr += opsize;
737
738 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
739 if (pCurInstrHC == NULL)
740 {
741 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
742 break;
743 }
744 Assert(VALID_PTR(pCurInstrHC));
745
746 cpu.mode = (pPage->fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
747 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
748 Assert(disret == true);
749 if (disret == false)
750 break;
751 }
752 break;
753 }
754
755 case OP_PUSH:
756 if (pCpu->pCurInstr->param1 != OP_PARM_REG_CS)
757 break;
758
759 /* no break */
760 case OP_STR:
761 case OP_LSL:
762 case OP_LAR:
763 case OP_SGDT:
764 case OP_SLDT:
765 case OP_SIDT:
766 case OP_SMSW:
767 case OP_VERW:
768 case OP_VERR:
769 case OP_CPUID:
770 case OP_IRET:
771#ifdef DEBUG
772 switch(pCpu->pCurInstr->opcode)
773 {
774 case OP_STR:
775 Log(("Privileged instruction at %VGv: str!!\n", pCurInstrGC));
776 break;
777 case OP_LSL:
778 Log(("Privileged instruction at %VGv: lsl!!\n", pCurInstrGC));
779 break;
780 case OP_LAR:
781 Log(("Privileged instruction at %VGv: lar!!\n", pCurInstrGC));
782 break;
783 case OP_SGDT:
784 Log(("Privileged instruction at %VGv: sgdt!!\n", pCurInstrGC));
785 break;
786 case OP_SLDT:
787 Log(("Privileged instruction at %VGv: sldt!!\n", pCurInstrGC));
788 break;
789 case OP_SIDT:
790 Log(("Privileged instruction at %VGv: sidt!!\n", pCurInstrGC));
791 break;
792 case OP_SMSW:
793 Log(("Privileged instruction at %VGv: smsw!!\n", pCurInstrGC));
794 break;
795 case OP_VERW:
796 Log(("Privileged instruction at %VGv: verw!!\n", pCurInstrGC));
797 break;
798 case OP_VERR:
799 Log(("Privileged instruction at %VGv: verr!!\n", pCurInstrGC));
800 break;
801 case OP_CPUID:
802 Log(("Privileged instruction at %VGv: cpuid!!\n", pCurInstrGC));
803 break;
804 case OP_PUSH:
805 Log(("Privileged instruction at %VGv: push cs!!\n", pCurInstrGC));
806 break;
807 case OP_IRET:
808 Log(("Privileged instruction at %VGv: iret!!\n", pCurInstrGC));
809 break;
810 }
811#endif
812
813 if (PATMR3HasBeenPatched(pVM, pCurInstrGC) == false)
814 {
815 rc = PATMR3InstallPatch(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
816 if (VBOX_FAILURE(rc))
817 {
818 Log(("PATMR3InstallPatch failed with %d\n", rc));
819 return VWRN_CONTINUE_ANALYSIS;
820 }
821 }
822 if (pCpu->pCurInstr->opcode == OP_IRET)
823 return VINF_SUCCESS; /* Look no further in this branch. */
824
825 return VWRN_CONTINUE_ANALYSIS;
826
827 case OP_JMP:
828 case OP_CALL:
829 {
830 // return or jump/call through a jump table
831 if (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J)
832 {
833#ifdef DEBUG
834 switch(pCpu->pCurInstr->opcode)
835 {
836 case OP_JMP:
837 Log(("Control Flow instruction at %VGv: jmp!!\n", pCurInstrGC));
838 break;
839 case OP_CALL:
840 Log(("Control Flow instruction at %VGv: call!!\n", pCurInstrGC));
841 break;
842 }
843#endif
844 return VWRN_CONTINUE_ANALYSIS;
845 }
846 return VWRN_CONTINUE_ANALYSIS;
847 }
848
849 }
850
851 return VWRN_CONTINUE_ANALYSIS;
852}
853
854#ifdef CSAM_ANALYSE_BEYOND_RET
855/**
856 * Wrapper for csamAnalyseCodeStream for call instructions.
857 *
858 * @returns VBox status code.
859 * @param pVM The VM to operate on.
860 * @param pInstrGC Guest context pointer to privileged instruction
861 * @param pCurInstrGC Guest context pointer to the current instruction
862 * @param fCode32 16 or 32 bits code
863 * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
864 * @param pUserData User pointer (callback specific)
865 *
866 */
867static int csamAnalyseCallCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
868 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
869{
870 int rc;
871 CSAMCALLEXITREC CallExitRec;
872 PCSAMCALLEXITREC pOldCallRec;
873 PCSAMPAGE pPage = 0;
874 uint32_t i;
875
876 CallExitRec.cInstrAfterRet = 0;
877
878 pOldCallRec = pCacheRec->pCallExitRec;
879 pCacheRec->pCallExitRec = &CallExitRec;
880
881 rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
882
883 for (i=0;i<CallExitRec.cInstrAfterRet;i++)
884 {
885 PCSAMPAGE pPage = 0;
886
887 pCurInstrGC = CallExitRec.pInstrAfterRetGC[i];
888
889 /* Check if we've previously encountered the instruction after the ret. */
890 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
891 {
892 DISCPUSTATE cpu;
893 uint32_t opsize;
894 uint8_t *pCurInstrHC = 0;
895 bool disret;
896#ifdef DEBUG
897 char szOutput[256];
898#endif
899 if (pPage == NULL)
900 {
901 /* New address; let's take a look at it. */
902 pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
903 if (pPage == NULL)
904 {
905 rc = VERR_NO_MEMORY;
906 goto done;
907 }
908 }
909
910 /**
911 * Some generic requirements for recognizing an adjacent function:
912 * - alignment fillers that consist of:
913 * - nop
914 * - lea genregX, [genregX (+ 0)]
915 * - push ebp after the filler (can extend this later); aligned at at least a 4 byte boundary
916 */
917 for (int j=0;j<16;j++)
918 {
919 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
920 if (pCurInstrHC == NULL)
921 {
922 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
923 goto done;
924 }
925 Assert(VALID_PTR(pCurInstrHC));
926
927 cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
928 STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
929#ifdef DEBUG
930 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
931 if (disret == true) Log(("CSAM Call Analysis: %s", szOutput));
932#else
933 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
934#endif
935 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeDisasm, a);
936 if (disret == false)
937 {
938 Log(("Disassembly failed at %VGv (probably page not present) -> return to caller\n", pCurInstrGC));
939 goto done;
940 }
941
942 STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
943
944 GCPTRTYPE(uint8_t *) addr = 0;
945 PCSAMPAGE pJmpPage = NULL;
946
947 if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
948 {
949 if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
950 {
951 /// @todo fault in the page
952 Log(("Page for current instruction %VGv is not present!!\n", pCurInstrGC));
953 goto done;
954 }
955 //all is fine, let's continue
956 csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
957 }
958
959 switch (cpu.pCurInstr->opcode)
960 {
961 case OP_NOP:
962 case OP_INT3:
963 break; /* acceptable */
964
965 case OP_LEA:
966 /* Must be similar to:
967 *
968 * lea esi, [esi]
969 * lea esi, [esi+0]
970 * Any register is allowed as long as source and destination are identical.
971 */
972 if ( cpu.param1.flags != USE_REG_GEN32
973 || ( cpu.param2.flags != USE_REG_GEN32
974 && ( !(cpu.param2.flags & USE_REG_GEN32)
975 || !(cpu.param2.flags & (USE_DISPLACEMENT8|USE_DISPLACEMENT16|USE_DISPLACEMENT32))
976 || cpu.param2.parval != 0
977 )
978 )
979 || cpu.param1.base.reg_gen32 != cpu.param2.base.reg_gen32
980 )
981 {
982 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
983 goto next_function;
984 }
985 break;
986
987 case OP_PUSH:
988 {
989 if ( (pCurInstrGC & 0x3) != 0
990 || cpu.param1.flags != USE_REG_GEN32
991 || cpu.param1.base.reg_gen32 != USE_REG_EBP
992 )
993 {
994 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
995 goto next_function;
996 }
997
998 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
999 {
1000 CSAMCALLEXITREC CallExitRec2;
1001 CallExitRec2.cInstrAfterRet = 0;
1002
1003 pCacheRec->pCallExitRec = &CallExitRec2;
1004
1005 /* Analyse the function. */
1006 Log(("Found new function at %VGv\n", pCurInstrGC));
1007 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
1008 csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
1009 }
1010 goto next_function;
1011 }
1012
1013 case OP_SUB:
1014 {
1015 if ( (pCurInstrGC & 0x3) != 0
1016 || cpu.param1.flags != USE_REG_GEN32
1017 || cpu.param1.base.reg_gen32 != USE_REG_ESP
1018 )
1019 {
1020 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
1021 goto next_function;
1022 }
1023
1024 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
1025 {
1026 CSAMCALLEXITREC CallExitRec2;
1027 CallExitRec2.cInstrAfterRet = 0;
1028
1029 pCacheRec->pCallExitRec = &CallExitRec2;
1030
1031 /* Analyse the function. */
1032 Log(("Found new function at %VGv\n", pCurInstrGC));
1033 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
1034 csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
1035 }
1036 goto next_function;
1037 }
1038
1039 default:
1040 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
1041 goto next_function;
1042 }
1043 /* Mark it as scanned. */
1044 csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
1045 pCurInstrGC += opsize;
1046 } /* for at most 16 instructions */
1047next_function:
1048 ; /* MSVC complains otherwise */
1049 }
1050 }
1051done:
1052 pCacheRec->pCallExitRec = pOldCallRec;
1053 return rc;
1054}
1055#else
1056#define csamAnalyseCallCodeStream csamAnalyseCodeStream
1057#endif
1058
1059/**
1060 * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
1061 *
1062 * @returns VBox status code.
1063 * @param pVM The VM to operate on.
1064 * @param pInstrGC Guest context pointer to privileged instruction
1065 * @param pCurInstrGC Guest context pointer to the current instruction
1066 * @param fCode32 16 or 32 bits code
1067 * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
1068 * @param pUserData User pointer (callback specific)
1069 *
1070 */
1071static int csamAnalyseCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
1072 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
1073{
1074 DISCPUSTATE cpu;
1075 PCSAMPAGE pPage = (PCSAMPAGE)pUserData;
1076 int rc = VWRN_CONTINUE_ANALYSIS;
1077 uint32_t opsize;
1078 HCPTRTYPE(uint8_t *) pCurInstrHC = 0;
1079 bool disret;
1080
1081#ifdef DEBUG
1082 char szOutput[256];
1083#endif
1084
1085 LogFlow(("csamAnalyseCodeStream: code at %VGv depth=%d\n", pCurInstrGC, pCacheRec->depth));
1086
1087 pVM->csam.s.fScanningStarted = true;
1088
1089 pCacheRec->depth++;
1090 /*
1091 * Limit the call depth. (rather arbitrary upper limit; too low and we won't detect certain
1092 * cpuid instructions in Linux kernels; too high and we waste too much time scanning code)
1093 * (512 is necessary to detect cpuid instructions in Red Hat EL4; see defect 1355)
1094 * @note we are using a lot of stack here. couple of 100k when we go to the full depth (!)
1095 */
1096 if (pCacheRec->depth > 512)
1097 {
1098 LogFlow(("CSAM: maximum calldepth reached for %VGv\n", pCurInstrGC));
1099 pCacheRec->depth--;
1100 return VINF_SUCCESS; //let's not go on forever
1101 }
1102
1103 Assert(!PATMIsPatchGCAddr(pVM, pCurInstrGC));
1104 csamR3CheckPageRecord(pVM, pCurInstrGC);
1105
1106 while(rc == VWRN_CONTINUE_ANALYSIS)
1107 {
1108 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
1109 {
1110 if (pPage == NULL)
1111 {
1112 /* New address; let's take a look at it. */
1113 pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
1114 if (pPage == NULL)
1115 {
1116 rc = VERR_NO_MEMORY;
1117 goto done;
1118 }
1119 }
1120 }
1121 else
1122 {
1123 LogFlow(("Code at %VGv has been scanned before\n", pCurInstrGC));
1124 rc = VINF_SUCCESS;
1125 goto done;
1126 }
1127
1128 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
1129 if (pCurInstrHC == NULL)
1130 {
1131 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
1132 rc = VERR_PATCHING_REFUSED;
1133 goto done;
1134 }
1135 Assert(VALID_PTR(pCurInstrHC));
1136
1137 cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
1138 STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
1139#ifdef DEBUG
1140 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
1141 if (disret == true) Log(("CSAM Analysis: %s", szOutput));
1142#else
1143 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
1144#endif
1145 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeDisasm, a);
1146 if (disret == false)
1147 {
1148 Log(("Disassembly failed at %VGv (probably page not present) -> return to caller\n", pCurInstrGC));
1149 rc = VINF_SUCCESS;
1150 goto done;
1151 }
1152
1153 STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
1154
1155 csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
1156
1157 GCPTRTYPE(uint8_t *) addr = 0;
1158 PCSAMPAGE pJmpPage = NULL;
1159
1160 if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
1161 {
1162 if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
1163 {
1164 /// @todo fault in the page
1165 Log(("Page for current instruction %VGv is not present!!\n", pCurInstrGC));
1166 rc = VWRN_CONTINUE_ANALYSIS;
1167 goto next_please;
1168 }
1169 //all is fine, let's continue
1170 csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
1171 }
1172 /*
1173 * If it's harmless, then don't bother checking it (the disasm tables had better be accurate!)
1174 */
1175 if ((cpu.pCurInstr->optype & ~OPTYPE_RRM_MASK) == OPTYPE_HARMLESS)
1176 {
1177 AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
1178 rc = VWRN_CONTINUE_ANALYSIS;
1179 goto next_please;
1180 }
1181
1182#ifdef CSAM_ANALYSE_BEYOND_RET
1183 /* Remember the address of the instruction following the ret in case the parent instruction was a call. */
1184 if ( pCacheRec->pCallExitRec
1185 && cpu.pCurInstr->opcode == OP_RETN
1186 && pCacheRec->pCallExitRec->cInstrAfterRet < CSAM_MAX_CALLEXIT_RET)
1187 {
1188 pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + opsize;
1189 pCacheRec->pCallExitRec->cInstrAfterRet++;
1190 }
1191#endif
1192
1193 rc = pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage);
1194 if (rc == VINF_SUCCESS)
1195 goto done;
1196
1197 // For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
1198 if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
1199 {
1200 addr = CSAMResolveBranch(&cpu, pCurInstrGC);
1201 if (addr == 0)
1202 {
1203 Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
1204 rc = VINF_SUCCESS;
1205 break;
1206 }
1207 Assert(!PATMIsPatchGCAddr(pVM, addr));
1208
1209 /* If the target address lies in a patch generated jump, then special action needs to be taken. */
1210 PATMR3DetectConflict(pVM, pCurInstrGC, addr);
1211
1212 /* Same page? */
1213 if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pCurInstrGC ))
1214 {
1215 if (!PGMGstIsPagePresent(pVM, addr))
1216 {
1217 Log(("Page for current instruction %VGv is not present!!\n", addr));
1218 rc = VWRN_CONTINUE_ANALYSIS;
1219 goto next_please;
1220 }
1221
1222 /* All is fine, let's continue. */
1223 csamR3CheckPageRecord(pVM, addr);
1224 }
1225
1226 pJmpPage = NULL;
1227 if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
1228 {
1229 if (pJmpPage == NULL)
1230 {
1231 /* New branch target; let's take a look at it. */
1232 pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
1233 if (pJmpPage == NULL)
1234 {
1235 rc = VERR_NO_MEMORY;
1236 goto done;
1237 }
1238 Assert(pPage);
1239 }
1240 if (cpu.pCurInstr->opcode == OP_CALL)
1241 rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1242 else
1243 rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1244
1245 if (rc != VINF_SUCCESS) {
1246 goto done;
1247 }
1248 }
1249 if (cpu.pCurInstr->opcode == OP_JMP)
1250 {//unconditional jump; return to caller
1251 rc = VINF_SUCCESS;
1252 goto done;
1253 }
1254
1255 rc = VWRN_CONTINUE_ANALYSIS;
1256 } //if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
1257#ifdef CSAM_SCAN_JUMP_TABLE
1258 else
1259 if ( cpu.pCurInstr->opcode == OP_JMP
1260 && (cpu.param1.flags & (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)) == (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)
1261 )
1262 {
1263 RTGCPTR pJumpTableGC = (RTGCPTR)cpu.param1.disp32;
1264 uint8_t *pJumpTableHC;
1265 int rc2;
1266
1267 Log(("Jump through jump table\n"));
1268
1269 rc2 = PGMPhysGCPtr2HCPtr(pVM, pJumpTableGC, (PRTHCPTR)&pJumpTableHC);
1270 if (rc2 == VINF_SUCCESS)
1271 {
1272 for (uint32_t i=0;i<2;i++)
1273 {
1274 uint64_t fFlags;
1275
1276 addr = pJumpTableGC + cpu.param1.scale * i;
1277 /* Same page? */
1278 if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pJumpTableGC))
1279 break;
1280
1281 addr = *(RTGCPTR *)(pJumpTableHC + cpu.param1.scale * i);
1282
1283 rc2 = PGMGstGetPage(pVM, addr, &fFlags, NULL);
1284 if ( rc2 != VINF_SUCCESS
1285 || (fFlags & X86_PTE_US)
1286 || !(fFlags & X86_PTE_P)
1287 )
1288 break;
1289
1290 Log(("Jump to %VGv\n", addr));
1291
1292 pJmpPage = NULL;
1293 if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
1294 {
1295 if (pJmpPage == NULL)
1296 {
1297 /* New branch target; let's take a look at it. */
1298 pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
1299 if (pJmpPage == NULL)
1300 {
1301 rc = VERR_NO_MEMORY;
1302 goto done;
1303 }
1304 Assert(pPage);
1305 }
1306 rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1307 if (rc != VINF_SUCCESS) {
1308 goto done;
1309 }
1310 }
1311 }
1312 }
1313 }
1314#endif
1315 if (rc != VWRN_CONTINUE_ANALYSIS) {
1316 break; //done!
1317 }
1318next_please:
1319 if (cpu.pCurInstr->opcode == OP_JMP)
1320 {
1321 rc = VINF_SUCCESS;
1322 goto done;
1323 }
1324 pCurInstrGC += opsize;
1325 }
1326done:
1327 pCacheRec->depth--;
1328 return rc;
1329}
1330
1331
1332/**
1333 * Calculates the 64 bits hash value for the current page
1334 *
1335 * @returns hash value
1336 * @param pVM The VM to operate on.
1337 * @param pInstr Page address
1338 */
1339uint64_t csamR3CalcPageHash(PVM pVM, RTGCPTR pInstr)
1340{
1341 uint64_t hash = 0;
1342 uint32_t val[5];
1343 int rc;
1344
1345 Assert((pInstr & PAGE_OFFSET_MASK) == 0);
1346
1347 rc = PGMPhysReadGCPtr(pVM, &val[0], pInstr, sizeof(val[0]));
1348 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1349 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1350 {
1351 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1352 return ~0ULL;
1353 }
1354
1355 rc = PGMPhysReadGCPtr(pVM, &val[1], pInstr+1024, sizeof(val[0]));
1356 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1357 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1358 {
1359 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1360 return ~0ULL;
1361 }
1362
1363 rc = PGMPhysReadGCPtr(pVM, &val[2], pInstr+2048, sizeof(val[0]));
1364 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1365 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1366 {
1367 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1368 return ~0ULL;
1369 }
1370
1371 rc = PGMPhysReadGCPtr(pVM, &val[3], pInstr+3072, sizeof(val[0]));
1372 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1373 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1374 {
1375 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1376 return ~0ULL;
1377 }
1378
1379 rc = PGMPhysReadGCPtr(pVM, &val[4], pInstr+4092, sizeof(val[0]));
1380 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1381 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1382 {
1383 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1384 return ~0ULL;
1385 }
1386
1387 // don't want to get division by zero traps
1388 val[2] |= 1;
1389 val[4] |= 1;
1390
1391 hash = (uint64_t)val[0] * (uint64_t)val[1] / (uint64_t)val[2] + (val[3]%val[4]);
1392 return (hash == ~0ULL) ? hash - 1 : hash;
1393}
1394
1395
1396/**
1397 * Notify CSAM of a page flush
1398 *
1399 * @returns VBox status code
1400 * @param pVM The VM to operate on.
1401 * @param addr GC address of the page to flush
1402 * @param fRemovePage Page removal flag
1403 */
1404static int csamFlushPage(PVM pVM, RTGCPTR addr, bool fRemovePage)
1405{
1406 PCSAMPAGEREC pPageRec;
1407 int rc;
1408 RTGCPHYS GCPhys = 0;
1409 uint64_t fFlags = 0;
1410
1411 if (!CSAMIsEnabled(pVM))
1412 return VINF_SUCCESS;
1413
1414 STAM_PROFILE_START(&pVM->csam.s.StatTimeFlushPage, a);
1415
1416 addr = addr & PAGE_BASE_GC_MASK;
1417
1418 /*
1419 * Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
1420 */
1421 if (pVM->csam.s.pPageTree == NULL)
1422 {
1423 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1424 return VWRN_CSAM_PAGE_NOT_FOUND;
1425 }
1426
1427 rc = PGMGstGetPage(pVM, addr, &fFlags, &GCPhys);
1428 /* Returned at a very early stage (no paging yet presumably). */
1429 if (rc == VERR_NOT_SUPPORTED)
1430 {
1431 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1432 return rc;
1433 }
1434
1435 if (VBOX_SUCCESS(rc))
1436 {
1437 if ( (fFlags & X86_PTE_US)
1438 || rc == VERR_PGM_PHYS_PAGE_RESERVED
1439 )
1440 {
1441 /* User page -> not relevant for us. */
1442 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
1443 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1444 return VINF_SUCCESS;
1445 }
1446 }
1447 else
1448 if (rc != VERR_PAGE_NOT_PRESENT && rc != VERR_PAGE_TABLE_NOT_PRESENT)
1449 AssertMsgFailed(("PGMR3GetPage %VGv failed with %Vrc\n", addr, rc));
1450
1451 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)addr);
1452 if (pPageRec)
1453 {
1454 if ( GCPhys == pPageRec->page.GCPhys
1455 && (fFlags & X86_PTE_P))
1456 {
1457 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
1458 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1459 return VINF_SUCCESS;
1460 }
1461
1462 Log(("CSAMR3FlushPage: page %VGv has changed -> FLUSH (rc=%Vrc) (Phys: %VGp vs %VGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
1463
1464 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushes, 1);
1465
1466 if (fRemovePage)
1467 csamRemovePageRecord(pVM, addr);
1468 else
1469 {
1470 CSAMMarkPage(pVM, addr, false);
1471 pPageRec->page.GCPhys = 0;
1472 pPageRec->page.fFlags = 0;
1473 rc = PGMGstGetPage(pVM, addr, &pPageRec->page.fFlags, &pPageRec->page.GCPhys);
1474 if (rc == VINF_SUCCESS)
1475 pPageRec->page.u64Hash = csamR3CalcPageHash(pVM, addr);
1476
1477 if (pPageRec->page.pBitmap == NULL)
1478 {
1479 pPageRec->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, CSAM_PAGE_BITMAP_SIZE);
1480 Assert(pPageRec->page.pBitmap);
1481 if (pPageRec->page.pBitmap == NULL)
1482 return VERR_NO_MEMORY;
1483 }
1484 else
1485 memset(pPageRec->page.pBitmap, 0, CSAM_PAGE_BITMAP_SIZE);
1486 }
1487
1488
1489 /*
1490 * Inform patch manager about the flush; no need to repeat the above check twice.
1491 */
1492 PATMR3FlushPage(pVM, addr);
1493
1494 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1495 return VINF_SUCCESS;
1496 }
1497 else
1498 {
1499 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1500 return VWRN_CSAM_PAGE_NOT_FOUND;
1501 }
1502}
1503
1504/**
1505 * Notify CSAM of a page flush
1506 *
1507 * @returns VBox status code
1508 * @param pVM The VM to operate on.
1509 * @param addr GC address of the page to flush
1510 */
1511CSAMR3DECL(int) CSAMR3FlushPage(PVM pVM, RTGCPTR addr)
1512{
1513 return csamFlushPage(pVM, addr, true /* remove page record */);
1514}
1515
1516/**
1517 * Remove a CSAM monitored page. Use with care!
1518 *
1519 * @returns VBox status code
1520 * @param pVM The VM to operate on.
1521 * @param addr GC address of the page to flush
1522 */
1523CSAMR3DECL(int) CSAMR3RemovePage(PVM pVM, RTGCPTR addr)
1524{
1525 PCSAMPAGEREC pPageRec;
1526 int rc;
1527
1528 addr = addr & PAGE_BASE_GC_MASK;
1529
1530 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)addr);
1531 if (pPageRec)
1532 {
1533 rc = csamRemovePageRecord(pVM, addr);
1534 if (VBOX_SUCCESS(rc))
1535 PATMR3FlushPage(pVM, addr);
1536 return VINF_SUCCESS;
1537 }
1538 return VWRN_CSAM_PAGE_NOT_FOUND;
1539}
1540
1541/**
1542 * Check a page record in case a page has been changed
1543 *
1544 * @returns VBox status code. (trap handled or not)
1545 * @param pVM The VM to operate on.
1546 * @param pInstrGC GC instruction pointer
1547 */
1548int csamR3CheckPageRecord(PVM pVM, RTGCPTR pInstrGC)
1549{
1550 PCSAMPAGEREC pPageRec;
1551 uint64_t u64hash;
1552
1553 pInstrGC = pInstrGC & PAGE_BASE_GC_MASK;
1554
1555 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstrGC);
1556 if (pPageRec)
1557 {
1558 u64hash = csamR3CalcPageHash(pVM, pInstrGC);
1559 if (u64hash != pPageRec->page.u64Hash)
1560 csamFlushPage(pVM, pInstrGC, false /* don't remove page record */);
1561 }
1562 else
1563 return VWRN_CSAM_PAGE_NOT_FOUND;
1564
1565 return VINF_SUCCESS;
1566}
1567
1568/**
1569 * Returns monitor description based on CSAM tag
1570 *
1571 * @return description string
1572 * @param enmTag Owner tag
1573 */
1574const char *csamGetMonitorDescription(CSAMTAG enmTag)
1575{
1576 if (enmTag == CSAM_TAG_PATM)
1577 return "CSAM-PATM self-modifying code monitor handler";
1578 else
1579 if (enmTag == CSAM_TAG_REM)
1580 return "CSAM-REM self-modifying code monitor handler";
1581 Assert(enmTag == CSAM_TAG_CSAM);
1582 return "CSAM self-modifying code monitor handler";
1583}
1584
1585/**
1586 * Adds page record to our lookup tree
1587 *
1588 * @returns CSAMPAGE ptr or NULL if failure
1589 * @param pVM The VM to operate on.
1590 * @param GCPtr Page address
1591 * @param enmTag Owner tag
1592 * @param fCode32 16 or 32 bits code
1593 * @param fMonitorInvalidation Monitor page invalidation flag
1594 */
1595static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTGCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
1596{
1597 PCSAMPAGEREC pPage;
1598 int rc;
1599 bool ret;
1600
1601 Log(("New page record for %VGv\n", GCPtr & PAGE_BASE_GC_MASK));
1602
1603 pPage = (PCSAMPAGEREC)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, sizeof(CSAMPAGEREC));
1604 if (pPage == NULL)
1605 {
1606 AssertMsgFailed(("csamCreatePageRecord: Out of memory!!!!\n"));
1607 return NULL;
1608 }
1609 /* Round down to page boundary. */
1610 GCPtr = (GCPtr & PAGE_BASE_GC_MASK);
1611 pPage->Core.Key = (AVLPVKEY)GCPtr;
1612 pPage->page.pPageGC = GCPtr;
1613 pPage->page.fCode32 = fCode32;
1614 pPage->page.fMonitorInvalidation = fMonitorInvalidation;
1615 pPage->page.enmTag = enmTag;
1616 pPage->page.fMonitorActive = false;
1617 pPage->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, PAGE_SIZE/sizeof(uint8_t));
1618 rc = PGMGstGetPage(pVM, GCPtr, &pPage->page.fFlags, &pPage->page.GCPhys);
1619 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1620
1621 pPage->page.u64Hash = csamR3CalcPageHash(pVM, GCPtr);
1622 ret = RTAvlPVInsert(&pVM->csam.s.pPageTree, &pPage->Core);
1623 Assert(ret);
1624
1625#ifdef CSAM_MONITOR_CODE_PAGES
1626 AssertRelease(!fInCSAMCodePageInvalidate);
1627
1628 switch (enmTag)
1629 {
1630 case CSAM_TAG_PATM:
1631 case CSAM_TAG_REM:
1632#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
1633 case CSAM_TAG_CSAM:
1634#endif
1635 {
1636 int rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
1637 (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
1638 csamGetMonitorDescription(enmTag));
1639 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VGv failed with %Vrc\n", GCPtr, rc));
1640 if (VBOX_FAILURE(rc))
1641 Log(("PGMR3HandlerVirtualRegisterEx for %VGv failed with %Vrc\n", GCPtr, rc));
1642
1643 /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
1644
1645 /* Prefetch it in case it's not there yet. */
1646 rc = PGMPrefetchPage(pVM, GCPtr);
1647 AssertRC(rc);
1648
1649 rc = PGMShwModifyPage(pVM, GCPtr, 1, 0, ~(uint64_t)X86_PTE_RW);
1650 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1651
1652 pPage->page.fMonitorActive = true;
1653 STAM_COUNTER_INC(&pVM->csam.s.StatPageMonitor);
1654 break;
1655 }
1656 default:
1657 break; /* to shut up GCC */
1658 }
1659
1660 Log(("csamCreatePageRecord %VGv HCPhys=%VGp\n", GCPtr, pPage->page.GCPhys));
1661
1662#ifdef VBOX_WITH_STATISTICS
1663 switch (enmTag)
1664 {
1665 case CSAM_TAG_CSAM:
1666 STAM_COUNTER_INC(&pVM->csam.s.StatPageCSAM);
1667 break;
1668 case CSAM_TAG_PATM:
1669 STAM_COUNTER_INC(&pVM->csam.s.StatPagePATM);
1670 break;
1671 case CSAM_TAG_REM:
1672 STAM_COUNTER_INC(&pVM->csam.s.StatPageREM);
1673 break;
1674 default:
1675 break; /* to shut up GCC */
1676 }
1677#endif
1678
1679#endif
1680
1681 STAM_COUNTER_INC(&pVM->csam.s.StatNrPages);
1682 if (fMonitorInvalidation)
1683 STAM_COUNTER_INC(&pVM->csam.s.StatNrPagesInv);
1684
1685 return &pPage->page;
1686}
1687
1688/**
1689 * Monitors a code page (if not already monitored)
1690 *
1691 * @returns VBox status code
1692 * @param pVM The VM to operate on.
1693 * @param pPageAddrGC The page to monitor
1694 * @param enmTag Monitor tag
1695 */
1696CSAMR3DECL(int) CSAMR3MonitorPage(PVM pVM, RTGCPTR pPageAddrGC, CSAMTAG enmTag)
1697{
1698 PCSAMPAGEREC pPageRec = NULL;
1699 int rc;
1700 bool fMonitorInvalidation;
1701
1702 /* Dirty pages must be handled before calling this function!. */
1703 Assert(!pVM->csam.s.cDirtyPages);
1704
1705 if (pVM->csam.s.fScanningStarted == false)
1706 return VINF_SUCCESS; /* too early */
1707
1708 pPageAddrGC &= PAGE_BASE_GC_MASK;
1709
1710 Log(("CSAMR3MonitorPage %VGv %d\n", pPageAddrGC, enmTag));
1711
1712 /** @todo implicit assumption */
1713 fMonitorInvalidation = (enmTag == CSAM_TAG_PATM);
1714
1715 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
1716 if (pPageRec == NULL)
1717 {
1718 uint64_t fFlags;
1719
1720 rc = PGMGstGetPage(pVM, pPageAddrGC, &fFlags, NULL);
1721 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1722 if ( rc == VINF_SUCCESS
1723 && (fFlags & X86_PTE_US))
1724 {
1725 /* We don't care about user pages. */
1726 STAM_COUNTER_INC(&pVM->csam.s.StatNrUserPages);
1727 return VINF_SUCCESS;
1728 }
1729
1730 csamCreatePageRecord(pVM, pPageAddrGC, enmTag, true /* 32 bits code */, fMonitorInvalidation);
1731
1732 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
1733 Assert(pPageRec);
1734 }
1735 /** @todo reference count */
1736
1737#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
1738 Assert(pPageRec->page.fMonitorActive);
1739#endif
1740
1741#ifdef CSAM_MONITOR_CODE_PAGES
1742 if (!pPageRec->page.fMonitorActive)
1743 {
1744 Log(("CSAMR3MonitorPage: activate monitoring for %VGv\n", pPageAddrGC));
1745
1746 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
1747 (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
1748 csamGetMonitorDescription(enmTag));
1749 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VGv failed with %Vrc\n", pPageAddrGC, rc));
1750 if (VBOX_FAILURE(rc))
1751 Log(("PGMR3HandlerVirtualRegisterEx for %VGv failed with %Vrc\n", pPageAddrGC, rc));
1752
1753 /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
1754
1755 /* Prefetch it in case it's not there yet. */
1756 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1757 AssertRC(rc);
1758
1759 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1760 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1761
1762 STAM_COUNTER_INC(&pVM->csam.s.StatPageMonitor);
1763
1764 pPageRec->page.fMonitorActive = true;
1765 pPageRec->page.fMonitorInvalidation = fMonitorInvalidation;
1766 }
1767 else
1768 if ( !pPageRec->page.fMonitorInvalidation
1769 && fMonitorInvalidation)
1770 {
1771 Assert(pPageRec->page.fMonitorActive);
1772 PGMHandlerVirtualChangeInvalidateCallback(pVM, pPageRec->page.pPageGC, CSAMCodePageInvalidate);
1773 pPageRec->page.fMonitorInvalidation = true;
1774 STAM_COUNTER_INC(&pVM->csam.s.StatNrPagesInv);
1775
1776 /* Prefetch it in case it's not there yet. */
1777 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1778 AssertRC(rc);
1779
1780 /* Make sure it's readonly. Page invalidation may have modified the attributes. */
1781 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1782 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1783 }
1784
1785#if 0 /* def VBOX_STRICT -> very annoying) */
1786 if (pPageRec->page.fMonitorActive)
1787 {
1788 uint64_t fPageShw;
1789 RTHCPHYS GCPhys;
1790 rc = PGMShwGetPage(pVM, pPageAddrGC, &fPageShw, &GCPhys);
1791// AssertMsg( (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1792// || !(fPageShw & X86_PTE_RW)
1793// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %VGv (%VHp) aren't readonly (%VX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
1794 }
1795#endif
1796
1797 if (pPageRec->page.GCPhys == 0)
1798 {
1799 /* Prefetch it in case it's not there yet. */
1800 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1801 AssertRC(rc);
1802 /* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
1803 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1804 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1805 }
1806#endif /* CSAM_MONITOR_CODE_PAGES */
1807 return VINF_SUCCESS;
1808}
1809
1810/**
1811 * Removes a page record from our lookup tree
1812 *
1813 * @returns VBox status code
1814 * @param pVM The VM to operate on.
1815 * @param GCPtr Page address
1816 */
1817static int csamRemovePageRecord(PVM pVM, RTGCPTR GCPtr)
1818{
1819 PCSAMPAGEREC pPageRec;
1820
1821 Log(("csamRemovePageRecord %VGv\n", GCPtr));
1822 pPageRec = (PCSAMPAGEREC)RTAvlPVRemove(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
1823
1824 if (pPageRec)
1825 {
1826 STAM_COUNTER_INC(&pVM->csam.s.StatNrRemovedPages);
1827
1828#ifdef CSAM_MONITOR_CODE_PAGES
1829 if (pPageRec->page.fMonitorActive)
1830 {
1831 /* @todo -> this is expensive (cr3 reload)!!!
1832 * if this happens often, then reuse it instead!!!
1833 */
1834 Assert(!fInCSAMCodePageInvalidate);
1835 STAM_COUNTER_DEC(&pVM->csam.s.StatPageMonitor);
1836 PGMHandlerVirtualDeregister(pVM, GCPtr);
1837 }
1838 if (pPageRec->page.enmTag == CSAM_TAG_PATM)
1839 {
1840 /* Make sure the recompiler flushes its cache as this page is no longer monitored. */
1841 STAM_COUNTER_INC(&pVM->csam.s.StatPageRemoveREMFlush);
1842 CPUMSetChangedFlags(pVM, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
1843 }
1844#endif
1845
1846#ifdef VBOX_WITH_STATISTICS
1847 switch (pPageRec->page.enmTag)
1848 {
1849 case CSAM_TAG_CSAM:
1850 STAM_COUNTER_DEC(&pVM->csam.s.StatPageCSAM);
1851 break;
1852 case CSAM_TAG_PATM:
1853 STAM_COUNTER_DEC(&pVM->csam.s.StatPagePATM);
1854 break;
1855 case CSAM_TAG_REM:
1856 STAM_COUNTER_DEC(&pVM->csam.s.StatPageREM);
1857 break;
1858 default:
1859 break; /* to shut up GCC */
1860 }
1861#endif
1862
1863 if (pPageRec->page.pBitmap) MMR3HeapFree(pPageRec->page.pBitmap);
1864 MMR3HeapFree(pPageRec);
1865 }
1866 else
1867 AssertFailed();
1868
1869 return VINF_SUCCESS;
1870}
1871
1872/**
1873 * Callback for delayed writes from non-EMT threads
1874 *
1875 * @param pVM VM Handle.
1876 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1877 * @param cbBuf How much it's reading/writing.
1878 */
1879static DECLCALLBACK(void) CSAMDelayedWriteHandler(PVM pVM, RTGCPTR GCPtr, size_t cbBuf)
1880{
1881 int rc = PATMR3PatchWrite(pVM, GCPtr, cbBuf);
1882 AssertRC(rc);
1883}
1884
1885/**
1886 * #PF Handler callback for virtual access handler ranges.
1887 *
1888 * Important to realize that a physical page in a range can have aliases, and
1889 * for ALL and WRITE handlers these will also trigger.
1890 *
1891 * @returns VINF_SUCCESS if the handler have carried out the operation.
1892 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1893 * @param pVM VM Handle.
1894 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1895 * @param pvPtr The HC mapping of that address.
1896 * @param pvBuf What the guest is reading/writing.
1897 * @param cbBuf How much it's reading/writing.
1898 * @param enmAccessType The access type.
1899 * @param pvUser User argument.
1900 */
1901static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1902{
1903 int rc;
1904
1905 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
1906 Log(("CSAMCodePageWriteHandler: write to %VGv size=%d\n", GCPtr, cbBuf));
1907
1908 if (VM_IS_EMT(pVM))
1909 {
1910 rc = PATMR3PatchWrite(pVM, GCPtr, cbBuf);
1911 }
1912 else
1913 {
1914 /* Queue the write instead otherwise we'll get concurrency issues. */
1915 /** @note in theory not correct to let it write the data first before disabling a patch!
1916 * (if it writes the same data as the patch jump and we replace it with obsolete opcodes)
1917 */
1918 Log(("CSAMCodePageWriteHandler: delayed write!\n"));
1919 rc = VMR3ReqCallEx(pVM, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID,
1920 (PFNRT)CSAMDelayedWriteHandler, 3, pVM, GCPtr, cbBuf);
1921 }
1922 AssertRC(rc);
1923
1924 return VINF_PGM_HANDLER_DO_DEFAULT;
1925}
1926
1927/**
1928 * #PF Handler callback for invalidation of virtual access handler ranges.
1929 *
1930 * @param pVM VM Handle.
1931 * @param GCPtr The virtual address the guest has changed.
1932 */
1933static DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr)
1934{
1935 fInCSAMCodePageInvalidate = true;
1936 LogFlow(("CSAMCodePageInvalidate %VGv\n", GCPtr));
1937 /** @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. */
1938 csamFlushPage(pVM, GCPtr, false /* don't remove page! */);
1939 fInCSAMCodePageInvalidate = false;
1940 return VINF_SUCCESS;
1941}
1942
1943/**
1944 * Check if the current instruction has already been checked before
1945 *
1946 * @returns VBox status code. (trap handled or not)
1947 * @param pVM The VM to operate on.
1948 * @param pInstr Instruction pointer
1949 * @param pPage CSAM patch structure pointer
1950 */
1951bool csamIsCodeScanned(PVM pVM, RTGCPTR pInstr, PCSAMPAGE *pPage)
1952{
1953 PCSAMPAGEREC pPageRec;
1954 uint32_t offset;
1955
1956 STAM_PROFILE_START(&pVM->csam.s.StatTimeCheckAddr, a);
1957
1958 offset = pInstr & PAGE_OFFSET_MASK;
1959 pInstr = pInstr & PAGE_BASE_GC_MASK;
1960
1961 Assert(pPage);
1962
1963 if (*pPage && (*pPage)->pPageGC == pInstr)
1964 {
1965 if ((*pPage)->pBitmap == NULL || ASMBitTest((*pPage)->pBitmap, offset))
1966 {
1967 STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
1968 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1969 return true;
1970 }
1971 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1972 return false;
1973 }
1974
1975 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstr);
1976 if (pPageRec)
1977 {
1978 if (pPage) *pPage= &pPageRec->page;
1979 if (pPageRec->page.pBitmap == NULL || ASMBitTest(pPageRec->page.pBitmap, offset))
1980 {
1981 STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
1982 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1983 return true;
1984 }
1985 }
1986 else
1987 {
1988 if (pPage) *pPage = NULL;
1989 }
1990 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1991 return false;
1992}
1993
1994/**
1995 * Mark an instruction in a page as scanned/not scanned
1996 *
1997 * @param pVM The VM to operate on.
1998 * @param pPage Patch structure pointer
1999 * @param pInstr Instruction pointer
2000 * @param opsize Instruction size
2001 * @param fScanned Mark as scanned or not
2002 */
2003static void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTGCPTR pInstr, uint32_t opsize, bool fScanned)
2004{
2005 LogFlow(("csamMarkCodeAsScanned %VGv opsize=%d\n", pInstr, opsize));
2006 CSAMMarkPage(pVM, pInstr, fScanned);
2007
2008 /** @todo should recreate empty bitmap if !fScanned */
2009 if (pPage->pBitmap == NULL)
2010 return;
2011
2012 if (fScanned)
2013 {
2014 // retn instructions can be scanned more than once
2015 if (ASMBitTest(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK) == 0)
2016 {
2017 pPage->uSize += opsize;
2018 STAM_COUNTER_ADD(&pVM->csam.s.StatNrInstr, 1);
2019 }
2020 if (pPage->uSize >= PAGE_SIZE)
2021 {
2022 Log(("Scanned full page (%VGv) -> free bitmap\n", pInstr & PAGE_BASE_GC_MASK));
2023 MMR3HeapFree(pPage->pBitmap);
2024 pPage->pBitmap = NULL;
2025 }
2026 else
2027 ASMBitSet(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
2028 }
2029 else
2030 ASMBitClear(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
2031}
2032
2033/**
2034 * Mark an instruction in a page as scanned/not scanned
2035 *
2036 * @returns VBox status code.
2037 * @param pVM The VM to operate on.
2038 * @param pInstr Instruction pointer
2039 * @param opsize Instruction size
2040 * @param fScanned Mark as scanned or not
2041 */
2042CSAMR3DECL(int) CSAMR3MarkCode(PVM pVM, RTGCPTR pInstr, uint32_t opsize, bool fScanned)
2043{
2044 PCSAMPAGE pPage = 0;
2045
2046 Assert(!fScanned); /* other case not implemented. */
2047 Assert(!PATMIsPatchGCAddr(pVM, pInstr));
2048
2049 if (csamIsCodeScanned(pVM, pInstr, &pPage) == false)
2050 {
2051 Assert(fScanned == true); /* other case should not be possible */
2052 return VINF_SUCCESS;
2053 }
2054
2055 Log(("CSAMR3MarkCode: %VGv size=%d fScanned=%d\n", pInstr, opsize, fScanned));
2056 csamMarkCode(pVM, pPage, pInstr, opsize, fScanned);
2057 return VINF_SUCCESS;
2058}
2059
2060
2061/**
2062 * Scan and analyse code
2063 *
2064 * @returns VBox status code.
2065 * @param pVM The VM to operate on.
2066 * @param Sel selector
2067 * @param pHiddenSel The hidden selector register.
2068 * @param pInstrGC Instruction pointer
2069 */
2070CSAMR3DECL(int) CSAMR3CheckCodeEx(PVM pVM, RTSEL Sel, CPUMSELREGHID *pHiddenSel, RTGCPTR pInstrGC)
2071{
2072 if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pInstrGC) == true)
2073 {
2074 // No use
2075 return VINF_SUCCESS;
2076 }
2077
2078 if (CSAMIsEnabled(pVM))
2079 {
2080 X86EFLAGS fakeflags;
2081
2082 /* we're not in v86 mode here */
2083 fakeflags.u32 = 0;
2084
2085 bool fCode32 = SELMIsSelector32Bit(pVM, fakeflags, Sel, pHiddenSel);
2086
2087 //assuming 32 bits code for now
2088 Assert(fCode32); NOREF(fCode32);
2089
2090 pInstrGC = SELMToFlat(pVM, fakeflags, Sel, pHiddenSel, pInstrGC);
2091
2092 return CSAMR3CheckCode(pVM, pInstrGC);
2093 }
2094 return VINF_SUCCESS;
2095}
2096
2097/**
2098 * Scan and analyse code
2099 *
2100 * @returns VBox status code.
2101 * @param pVM The VM to operate on.
2102 * @param pInstrGC Instruction pointer (0:32 virtual address)
2103 */
2104CSAMR3DECL(int) CSAMR3CheckCode(PVM pVM, RTGCPTR pInstrGC)
2105{
2106 int rc;
2107 PCSAMPAGE pPage = NULL;
2108
2109 if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pInstrGC) == true)
2110 {
2111 // No use
2112 return VINF_SUCCESS;
2113 }
2114
2115 if (CSAMIsEnabled(pVM))
2116 {
2117 // Cache record for PATMGCVirtToHCVirt
2118 CSAMP2GLOOKUPREC cacheRec = {0};
2119
2120 STAM_PROFILE_START(&pVM->csam.s.StatTime, a);
2121 rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
2122 STAM_PROFILE_STOP(&pVM->csam.s.StatTime, a);
2123 if (rc != VINF_SUCCESS)
2124 {
2125 Log(("csamAnalyseCodeStream failed with %d\n", rc));
2126 return rc;
2127 }
2128 }
2129 return VINF_SUCCESS;
2130}
2131
2132/**
2133 * Flush dirty code pages
2134 *
2135 * @returns VBox status code.
2136 * @param pVM The VM to operate on.
2137 */
2138static int csamR3FlushDirtyPages(PVM pVM)
2139{
2140 STAM_PROFILE_START(&pVM->csam.s.StatFlushDirtyPages, a);
2141
2142 for (uint32_t i=0;i<pVM->csam.s.cDirtyPages;i++)
2143 {
2144 int rc;
2145 PCSAMPAGEREC pPageRec;
2146 RTGCPTR GCPtr = pVM->csam.s.pvDirtyBasePage[i];
2147
2148 GCPtr = GCPtr & PAGE_BASE_GC_MASK;
2149
2150 /* Notify the recompiler that this page has been changed. */
2151 REMR3NotifyCodePageChanged(pVM, GCPtr);
2152
2153 /* Enable write protection again. (use the fault address as it might be an alias) */
2154 rc = PGMShwModifyPage(pVM, pVM->csam.s.pvDirtyFaultPage[i], 1, 0, ~(uint64_t)X86_PTE_RW);
2155 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2156
2157 Log(("CSAMR3FlushDirtyPages: flush %VGv (modifypage rc=%Vrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
2158
2159 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
2160 if (pPageRec && pPageRec->page.enmTag == CSAM_TAG_REM)
2161 {
2162 uint64_t fFlags;
2163
2164 rc = PGMGstGetPage(pVM, GCPtr, &fFlags, NULL);
2165 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
2166 if ( rc == VINF_SUCCESS
2167 && (fFlags & X86_PTE_US))
2168 {
2169 /* We don't care about user pages. */
2170 csamRemovePageRecord(pVM, GCPtr);
2171 STAM_COUNTER_INC(&pVM->csam.s.StatNrUserPages);
2172 }
2173 }
2174 }
2175 pVM->csam.s.cDirtyPages = 0;
2176 STAM_PROFILE_STOP(&pVM->csam.s.StatFlushDirtyPages, a);
2177 return VINF_SUCCESS;
2178}
2179
2180/**
2181 * Flush potential new code pages
2182 *
2183 * @returns VBox status code.
2184 * @param pVM The VM to operate on.
2185 */
2186static int csamR3FlushCodePages(PVM pVM)
2187{
2188 for (uint32_t i=0;i<pVM->csam.s.cPossibleCodePages;i++)
2189 {
2190 RTGCPTR GCPtr = pVM->csam.s.pvPossibleCodePage[i];
2191
2192 GCPtr = GCPtr & PAGE_BASE_GC_MASK;
2193
2194 Log(("csamR3FlushCodePages: %VGv\n", GCPtr));
2195 PGMShwSetPage(pVM, GCPtr, 1, 0);
2196 /* Resync the page to make sure instruction fetch will fault */
2197 CSAMMarkPage(pVM, GCPtr, false);
2198 }
2199 pVM->csam.s.cPossibleCodePages = 0;
2200 return VINF_SUCCESS;
2201}
2202
2203/**
2204 * Perform any pending actions
2205 *
2206 * @returns VBox status code.
2207 * @param pVM The VM to operate on.
2208 */
2209CSAMR3DECL(int) CSAMR3DoPendingAction(PVM pVM)
2210{
2211 csamR3FlushDirtyPages(pVM);
2212 csamR3FlushCodePages(pVM);
2213
2214 VM_FF_CLEAR(pVM, VM_FF_CSAM_PENDING_ACTION);
2215 return VINF_SUCCESS;
2216}
2217
2218/**
2219 * Analyse interrupt and trap gates
2220 *
2221 * @returns VBox status code.
2222 * @param pVM The VM to operate on.
2223 * @param iGate Start gate
2224 * @param cGates Number of gates to check
2225 */
2226CSAMR3DECL(int) CSAMR3CheckGates(PVM pVM, uint32_t iGate, uint32_t cGates)
2227{
2228 uint16_t cbIDT;
2229 RTGCPTR GCPtrIDT = CPUMGetGuestIDTR(pVM, &cbIDT);
2230 uint32_t iGateEnd;
2231 uint32_t maxGates;
2232 VBOXIDTE aIDT[256];
2233 PVBOXIDTE pGuestIdte;
2234 int rc;
2235
2236 if (EMIsRawRing0Enabled(pVM) == false)
2237 {
2238 /* Enabling interrupt gates only works when raw ring 0 is enabled. */
2239 //AssertFailed();
2240 return VINF_SUCCESS;
2241 }
2242
2243 /* We only check all gates once during a session */
2244 if ( !pVM->csam.s.fGatesChecked
2245 && cGates != 256)
2246 return VINF_SUCCESS; /* too early */
2247
2248 /* We only check all gates once during a session */
2249 if ( pVM->csam.s.fGatesChecked
2250 && cGates != 1)
2251 return VINF_SUCCESS; /* ignored */
2252
2253 Assert(cGates <= 256);
2254 if (!GCPtrIDT || cGates > 256)
2255 return VERR_INVALID_PARAMETER;
2256
2257 if (cGates != 1)
2258 pVM->csam.s.fGatesChecked = true;
2259
2260 /* Determine valid upper boundary. */
2261 maxGates = (cbIDT+1) / sizeof(VBOXIDTE);
2262 Assert(iGate < maxGates);
2263 if (iGate > maxGates)
2264 return VERR_INVALID_PARAMETER;
2265
2266 if (iGate + cGates > maxGates)
2267 cGates = maxGates - iGate;
2268
2269 GCPtrIDT = GCPtrIDT + iGate * sizeof(VBOXIDTE);
2270 iGateEnd = iGate + cGates;
2271
2272 STAM_PROFILE_START(&pVM->csam.s.StatCheckGates, a);
2273
2274 /*
2275 * Get IDT entries.
2276 */
2277 if (PAGE_ADDRESS(GCPtrIDT) == PAGE_ADDRESS(GCPtrIDT+cGates*sizeof(VBOXIDTE)))
2278 {
2279 /* Just convert the IDT address to a HC pointer. The whole IDT fits in one page. */
2280 rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrIDT, (PRTHCPTR)&pGuestIdte);
2281 if (VBOX_FAILURE(rc))
2282 {
2283 AssertMsgRC(rc, ("Failed to read IDTE! rc=%Vrc\n", rc));
2284 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2285 return rc;
2286 }
2287 }
2288 else
2289 {
2290 /* Slow method when it crosses a page boundary. */
2291 rc = PGMPhysReadGCPtr(pVM, aIDT, GCPtrIDT, cGates*sizeof(VBOXIDTE));
2292 if (VBOX_FAILURE(rc))
2293 {
2294 AssertMsgRC(rc, ("Failed to read IDTE! rc=%Vrc\n", rc));
2295 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2296 return rc;
2297 }
2298 pGuestIdte = &aIDT[0];
2299 }
2300
2301 for (/*iGate*/; iGate<iGateEnd; iGate++, pGuestIdte++)
2302 {
2303 Assert(TRPMR3GetGuestTrapHandler(pVM, iGate) == TRPM_INVALID_HANDLER);
2304
2305 if ( pGuestIdte->Gen.u1Present
2306 && (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
2307 && (pGuestIdte->Gen.u2DPL == 3 || pGuestIdte->Gen.u2DPL == 0)
2308 )
2309 {
2310 RTGCPTR pHandler;
2311 CSAMP2GLOOKUPREC cacheRec = {0}; /* Cache record for PATMGCVirtToHCVirt. */
2312 PCSAMPAGE pPage = NULL;
2313 X86EFLAGS fakeflags;
2314 SELMSELINFO selInfo;
2315
2316 /* we're not in v86 mode here */
2317 fakeflags.u32 = 0;
2318
2319 pHandler = (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow;
2320 pHandler = SELMToFlat(pVM, fakeflags, pGuestIdte->Gen.u16SegSel, 0, pHandler);
2321
2322 rc = SELMR3GetSelectorInfo(pVM, pGuestIdte->Gen.u16SegSel, &selInfo);
2323 if ( VBOX_FAILURE(rc)
2324 || selInfo.GCPtrBase != 0
2325 || selInfo.cbLimit != ~0U
2326 )
2327 {
2328 /* Refuse to patch a handler whose idt cs selector isn't wide open. */
2329 Log(("CSAMCheckGates: check gate %d failed due to rc %Vrc GCPtrBase=%VGv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
2330 continue;
2331 }
2332
2333
2334 if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
2335 {
2336 Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %VGv)\n", iGate, pGuestIdte->Gen.u16SegSel, (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow, pHandler));
2337 }
2338 else
2339 {
2340 Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %VGv)\n", iGate, pGuestIdte->Gen.u16SegSel, (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow, pHandler));
2341 }
2342
2343 STAM_PROFILE_START(&pVM->csam.s.StatTime, a);
2344 rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
2345 STAM_PROFILE_STOP(&pVM->csam.s.StatTime, a);
2346 if (rc != VINF_SUCCESS)
2347 {
2348 Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
2349 continue;
2350 }
2351 /* OpenBSD guest specific patch test. */
2352 if (iGate >= 0x20)
2353 {
2354 PCPUMCTX pCtx;
2355 DISCPUSTATE cpu;
2356 RTGCUINTPTR aOpenBsdPushCSOffset[3] = {0x03, /* OpenBSD 3.7 & 3.8 */
2357 0x2B, /* OpenBSD 4.0 installation ISO */
2358 0x2F}; /* OpenBSD 4.0 after install */
2359
2360 rc = CPUMQueryGuestCtxPtr(pVM, &pCtx);
2361 AssertRC(rc); /* can't fail */
2362
2363 for (unsigned i=0;i<ELEMENTS(aOpenBsdPushCSOffset);i++)
2364 {
2365 rc = CPUMR3DisasmInstrCPU(pVM, pCtx, pHandler - aOpenBsdPushCSOffset[i], &cpu, NULL);
2366 if ( rc == VINF_SUCCESS
2367 && cpu.pCurInstr->opcode == OP_PUSH
2368 && cpu.pCurInstr->param1 == OP_PARM_REG_CS)
2369 {
2370 rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
2371 if (VBOX_SUCCESS(rc))
2372 Log(("Installed OpenBSD interrupt handler prefix instruction (push cs) patch\n"));
2373 }
2374 }
2375 }
2376
2377 /* Trap gates and certain interrupt gates. */
2378 uint32_t fPatchFlags = PATMFL_CODE32 | PATMFL_IDTHANDLER;
2379
2380 if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
2381 fPatchFlags |= PATMFL_TRAPHANDLER;
2382 else
2383 fPatchFlags |= PATMFL_INTHANDLER;
2384
2385 switch (iGate) {
2386 case 8:
2387 case 10:
2388 case 11:
2389 case 12:
2390 case 13:
2391 case 14:
2392 case 17:
2393 fPatchFlags |= PATMFL_TRAPHANDLER_WITH_ERRORCODE;
2394 break;
2395 default:
2396 /* No error code. */
2397 break;
2398 }
2399
2400 Log(("Installing %s gate handler for 0x%X at %VGv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
2401
2402 rc = PATMR3InstallPatch(pVM, pHandler, fPatchFlags);
2403 if (VBOX_SUCCESS(rc) || rc == VERR_PATM_ALREADY_PATCHED)
2404 {
2405 Log(("Gate handler 0x%X is SAFE!\n", iGate));
2406
2407 RTGCPTR pNewHandlerGC = PATMR3QueryPatchGCPtr(pVM, pHandler);
2408 if (pNewHandlerGC)
2409 {
2410 rc = TRPMR3SetGuestTrapHandler(pVM, iGate, pNewHandlerGC);
2411 if (VBOX_FAILURE(rc))
2412 Log(("TRPMR3SetGuestTrapHandler %d failed with %Vrc\n", iGate, rc));
2413 }
2414 }
2415 }
2416 } /* for */
2417 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2418 return VINF_SUCCESS;
2419}
2420
2421/**
2422 * Query CSAM state (enabled/disabled)
2423 *
2424 * @returns 0 - disabled, 1 - enabled
2425 * @param pVM The VM to operate on.
2426 */
2427CSAMR3DECL(int) CSAMR3IsEnabled(PVM pVM)
2428{
2429 return pVM->fCSAMEnabled;
2430}
2431
2432#ifdef VBOX_WITH_DEBUGGER
2433/**
2434 * The '.csamoff' command.
2435 *
2436 * @returns VBox status.
2437 * @param pCmd Pointer to the command descriptor (as registered).
2438 * @param pCmdHlp Pointer to command helper functions.
2439 * @param pVM Pointer to the current VM (if any).
2440 * @param paArgs Pointer to (readonly) array of arguments.
2441 * @param cArgs Number of arguments in the array.
2442 */
2443static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2444{
2445 /*
2446 * Validate input.
2447 */
2448 if (!pVM)
2449 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
2450
2451 CSAMDisableScanning(pVM);
2452 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "CSAM Scanning disabled\n");
2453}
2454
2455/**
2456 * The '.csamon' command.
2457 *
2458 * @returns VBox status.
2459 * @param pCmd Pointer to the command descriptor (as registered).
2460 * @param pCmdHlp Pointer to command helper functions.
2461 * @param pVM Pointer to the current VM (if any).
2462 * @param paArgs Pointer to (readonly) array of arguments.
2463 * @param cArgs Number of arguments in the array.
2464 */
2465static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2466{
2467 /*
2468 * Validate input.
2469 */
2470 if (!pVM)
2471 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
2472
2473 CSAMEnableScanning(pVM);
2474 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "CSAM Scanning enabled\n");
2475}
2476#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette