VirtualBox

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

Last change on this file since 1833 was 1833, checked in by vboxsync, 18 years ago

comment + logging

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