VirtualBox

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

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

SELM function changes for v86 mode code.
CPL check fixes for V86 mode code.

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