VirtualBox

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

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

Rewrote and simplified interrupt handler patching.

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