VirtualBox

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

Last change on this file since 19167 was 19141, checked in by vboxsync, 16 years ago

Action flags breakup.
Fixed PGM saved state loading of 2.2.2 images.
Reduced hacks in PATM state loading (fixups).

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