VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/CSAMAll.cpp@ 62517

Last change on this file since 62517 was 62478, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.8 KB
Line 
1/* $Id: CSAMAll.cpp 62478 2016-07-22 18:29:06Z vboxsync $ */
2/** @file
3 * CSAM - Guest OS Code Scanning and Analysis Manager - Any Context
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_CSAM
23#include <VBox/vmm/cpum.h>
24#include <VBox/vmm/stam.h>
25#include <VBox/vmm/patm.h>
26#include <VBox/vmm/csam.h>
27#include <VBox/vmm/pgm.h>
28#include <VBox/vmm/em.h>
29#include <VBox/vmm/hm.h>
30#include <VBox/vmm/mm.h>
31#ifdef VBOX_WITH_REM
32# include <VBox/vmm/rem.h>
33#endif
34#include <VBox/sup.h>
35#include <VBox/vmm/mm.h>
36#include <VBox/param.h>
37#include <iprt/avl.h>
38#include "CSAMInternal.h"
39#include <VBox/vmm/vm.h>
40#include <VBox/vmm/vmm.h>
41#include <VBox/dbg.h>
42#include <VBox/err.h>
43#include <VBox/log.h>
44#include <VBox/dis.h>
45#include <VBox/disopcode.h>
46#include <iprt/assert.h>
47#include <iprt/asm.h>
48#include <iprt/string.h>
49
50#ifdef IN_RING0
51# error "IN_RING3 & IN_RC only!"
52#endif
53
54
55/**
56 * @callback_method_impl{FNPGMVIRTHANDLER,
57 * Access handler callback for virtual access handler ranges.}
58 */
59PGM_ALL_CB2_DECL(VBOXSTRICTRC)
60csamCodePageWriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf,
61 PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
62{
63 Log(("csamCodePageWriteHandler: write to %RGv LB %zu\n", GCPtr, cbBuf));
64 Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType);
65 Assert(VMCPU_IS_EMT(pVCpu));
66
67 /*
68 * Check if it's a dummy write that doesn't change anything.
69 */
70 if ( PAGE_ADDRESS(pvPtr) == PAGE_ADDRESS((uintptr_t)pvPtr + cbBuf - 1)
71 && !memcmp(pvPtr, pvBuf, cbBuf))
72 {
73 Log(("csamCodePageWriteHandler: dummy write -> ignore\n"));
74 return VINF_PGM_HANDLER_DO_DEFAULT;
75 }
76
77#ifdef IN_RING3
78 /*
79 * Ring-3: Do proper handling.
80 */
81 int rc = PATMR3PatchWrite(pVM, GCPtr, (uint32_t)cbBuf);
82 AssertRC(rc);
83 return VINF_PGM_HANDLER_DO_DEFAULT;
84
85#else
86 /*
87 * Raw-mode: Try avoid needing to go to ring-3 (same as csamRCCodePageWritePfHandler).
88 */
89 uint32_t const cpl = CPUMGetGuestCPL(pVCpu);
90 bool const fPatchCode = PATMIsPatchGCAddr(pVM, CPUMGetGuestRIP(pVCpu));
91 PPATMGCSTATE pPATMGCState = PATMGetGCState(pVM);
92
93 Assert(pVM->csam.s.cDirtyPages < CSAM_MAX_DIRTY_PAGES);
94 Assert(pPATMGCState);
95 Assert(pPATMGCState->fPIF || fPatchCode);
96
97# ifdef VBOX_WITH_REM
98 /* Flush the recompilers translation block cache as the guest seems to be modifying instructions. */
99 /** @todo a bit overkill?? */
100 REMFlushTBs(pVM);
101# endif
102
103 /*
104 * When patch code is executing instructions that must complete, then we
105 * must *never* interrupt it.
106 */
107 if (!pPATMGCState->fPIF && fPatchCode)
108 {
109 Log(("csamRCCodePageWriteHandler: fPIF=0 -> stack fault in patch generated code at %08RX32!\n", CPUMGetGuestRIP(pVCpu)));
110 return VINF_PGM_HANDLER_DO_DEFAULT;
111 }
112
113 Log(("csamRCCodePageWriteHandler: code page write at %RGv (cpl=%d)\n", GCPtr, cpl));
114
115 /*
116 * If user code is modifying one of our monitored pages, then we can safely
117 * write to it as it's no longer being used for supervisor code.
118 */
119 if (cpl != 3)
120 {
121 VBOXSTRICTRC rcStrict = PATMRCHandleWriteToPatchPage(pVM, NULL /* pRegFrame = no interpret */, (RTRCPTR)GCPtr, cbBuf);
122 if ( rcStrict == VINF_PGM_HANDLER_DO_DEFAULT
123 || rcStrict == VINF_SUCCESS)
124 return rcStrict;
125 if (rcStrict == VINF_EM_RAW_EMULATE_INSTR)
126 {
127 STAM_COUNTER_INC(&pVM->csam.s.StatDangerousWrite);
128 return VINF_EM_RAW_EMULATE_INSTR;
129 }
130 Assert(rcStrict == VERR_PATCH_NOT_FOUND);
131 }
132
133 /*
134 * Schedule ring-3 activity.
135 * Note that GCPtr might be a different address in case of aliases. So,
136 * take down both alternatives.
137 */
138 VMCPU_FF_SET(pVCpu, VMCPU_FF_CSAM_PENDING_ACTION);
139 pVM->csam.s.pvDirtyBasePage[pVM->csam.s.cDirtyPages] = (RTRCPTR)GCPtr;
140 pVM->csam.s.pvDirtyFaultPage[pVM->csam.s.cDirtyPages] = (RTRCPTR)GCPtr;
141 if (++pVM->csam.s.cDirtyPages == CSAM_MAX_DIRTY_PAGES)
142 return VINF_CSAM_PENDING_ACTION;
143
144 /*
145 * Continue with the write. The VM_FF_CSAM_FLUSH_DIRTY_PAGE handler will reset it to readonly again.
146 */
147 Log(("csamRCCodePageWriteHandler: enabled r/w for page %RGv (%RGv)\n", GCPtr, GCPtr));
148 STAM_COUNTER_INC(&pVM->csam.s.StatCodePageModified);
149 return VINF_PGM_HANDLER_DO_DEFAULT;
150#endif
151}
152
153
154/**
155 * Check if this page needs to be analysed by CSAM
156 *
157 * @returns VBox status code
158 * @param pVM The cross context VM structure.
159 * @param pvFault Fault address
160 */
161VMM_INT_DECL(int) CSAMExecFault(PVM pVM, RTRCPTR pvFault)
162{
163 Assert(!HMIsEnabled(pVM));
164 if (!CSAMIsEnabled(pVM))
165 return VINF_SUCCESS;
166
167 LogFlow(("CSAMGCExecFault: for page %08X scanned=%d\n", pvFault, CSAMIsPageScanned(pVM, pvFault)));
168
169 if (CSAMIsPageScanned(pVM, pvFault))
170 {
171 // Already checked!
172 STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesGC, 1);
173 return VINF_SUCCESS;
174 }
175
176 STAM_COUNTER_ADD(&pVM->csam.s.StatNrTraps, 1);
177 VMCPU_FF_SET(VMMGetCpu0(pVM), VMCPU_FF_CSAM_SCAN_PAGE);
178 return VINF_CSAM_PENDING_ACTION;
179}
180
181
182/**
183 * Check if this page was previously scanned by CSAM
184 *
185 * @returns true -> scanned, false -> not scanned
186 * @param pVM The cross context VM structure.
187 * @param pPage GC page address
188 */
189VMM_INT_DECL(bool) CSAMIsPageScanned(PVM pVM, RTRCPTR pPage)
190{
191 int pgdir, bit;
192 uintptr_t page;
193 Assert(!HMIsEnabled(pVM));
194
195 page = (uintptr_t)pPage;
196 pgdir = page >> X86_PAGE_4M_SHIFT;
197 bit = (page & X86_PAGE_4M_OFFSET_MASK) >> X86_PAGE_4K_SHIFT;
198
199 Assert(pgdir < CSAM_PGDIRBMP_CHUNKS);
200 Assert(bit < PAGE_SIZE);
201
202 return pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir] && ASMBitTest((void *)pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
203}
204
205
206
207/**
208 * Mark a page as scanned/not scanned
209 *
210 * @note: we always mark it as scanned, even if we haven't completely done so
211 *
212 * @returns VBox status code.
213 * @param pVM The cross context VM structure.
214 * @param pPage GC page address (not necessarily aligned)
215 * @param fScanned Mark as scanned or not scanned
216 *
217 */
218VMM_INT_DECL(int) CSAMMarkPage(PVM pVM, RTRCUINTPTR pPage, bool fScanned)
219{
220 int pgdir, bit;
221 uintptr_t page;
222
223#ifdef LOG_ENABLED
224 if (fScanned && !CSAMIsPageScanned(pVM, (RTRCPTR)pPage))
225 Log(("CSAMMarkPage %RRv\n", pPage));
226#endif
227
228 if (!CSAMIsEnabled(pVM))
229 return VINF_SUCCESS;
230 Assert(!HMIsEnabled(pVM));
231
232 page = (uintptr_t)pPage;
233 pgdir = page >> X86_PAGE_4M_SHIFT;
234 bit = (page & X86_PAGE_4M_OFFSET_MASK) >> X86_PAGE_4K_SHIFT;
235
236 Assert(pgdir < CSAM_PGDIRBMP_CHUNKS);
237 Assert(bit < PAGE_SIZE);
238
239 if(!CTXSUFF(pVM->csam.s.pPDBitmap)[pgdir])
240 {
241 STAM_COUNTER_INC(&pVM->csam.s.StatBitmapAlloc);
242 int rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir]);
243 if (RT_FAILURE(rc))
244 {
245 Log(("MMHyperAlloc failed with %Rrc\n", rc));
246 return rc;
247 }
248#ifdef IN_RC
249 pVM->csam.s.pPDHCBitmapGC[pgdir] = MMHyperRCToR3(pVM, (RCPTRTYPE(void*))pVM->csam.s.pPDBitmapGC[pgdir]);
250 if (!pVM->csam.s.pPDHCBitmapGC[pgdir])
251 {
252 Log(("MMHyperHC2GC failed for %RRv\n", pVM->csam.s.pPDBitmapGC[pgdir]));
253 return rc;
254 }
255#else
256 pVM->csam.s.pPDGCBitmapHC[pgdir] = MMHyperR3ToRC(pVM, pVM->csam.s.pPDBitmapHC[pgdir]);
257 if (!pVM->csam.s.pPDGCBitmapHC[pgdir])
258 {
259 Log(("MMHyperHC2GC failed for %RHv\n", pVM->csam.s.pPDBitmapHC[pgdir]));
260 return rc;
261 }
262#endif
263 }
264 if(fScanned)
265 ASMBitSet((void *)pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
266 else
267 ASMBitClear((void *)pVM->csam.s.CTXSUFF(pPDBitmap)[pgdir], bit);
268
269 return VINF_SUCCESS;
270}
271
272/**
273 * Check if this page needs to be analysed by CSAM.
274 *
275 * This function should only be called for supervisor pages and
276 * only when CSAM is enabled. Leaving these selection criteria
277 * to the caller simplifies the interface (PTE passing).
278 *
279 * Note that the page has not yet been synced, so the TLB trick
280 * (which wasn't ever active anyway) cannot be applied.
281 *
282 * @returns true if the page should be marked not present because
283 * CSAM want need to scan it.
284 * @returns false if the page was already scanned.
285 * @param pVM The cross context VM structure.
286 * @param GCPtr GC pointer of page
287 */
288VMM_INT_DECL(bool) CSAMDoesPageNeedScanning(PVM pVM, RTRCUINTPTR GCPtr)
289{
290 if (!CSAMIsEnabled(pVM))
291 return false;
292 Assert(!HMIsEnabled(pVM));
293
294 if(CSAMIsPageScanned(pVM, (RTRCPTR)GCPtr))
295 {
296 /* Already checked! */
297 STAM_COUNTER_ADD(&CTXSUFF(pVM->csam.s.StatNrKnownPages), 1);
298 return false;
299 }
300 STAM_COUNTER_ADD(&CTXSUFF(pVM->csam.s.StatNrPageNP), 1);
301 return true;
302}
303
304
305/**
306 * Remember a possible code page for later inspection
307 *
308 * @returns VBox status code.
309 * @param pVM The cross context VM structure.
310 * @param GCPtr GC pointer of page
311 */
312VMM_INT_DECL(void) CSAMMarkPossibleCodePage(PVM pVM, RTRCPTR GCPtr)
313{
314 Assert(!HMIsEnabled(pVM));
315 if (pVM->csam.s.cPossibleCodePages < RT_ELEMENTS(pVM->csam.s.pvPossibleCodePage))
316 {
317 pVM->csam.s.pvPossibleCodePage[pVM->csam.s.cPossibleCodePages++] = (RTRCPTR)GCPtr;
318 VMCPU_FF_SET(VMMGetCpu0(pVM), VMCPU_FF_CSAM_PENDING_ACTION);
319 }
320 return;
321}
322
323
324/**
325 * Turn on code scanning
326 *
327 * @returns VBox status code.
328 * @param pVM The cross context VM structure.
329 */
330VMM_INT_DECL(int) CSAMEnableScanning(PVM pVM)
331{
332 AssertReturn(!HMIsEnabled(pVM), VERR_CSAM_HM_IPE);
333 pVM->fCSAMEnabled = true;
334 return VINF_SUCCESS;
335}
336
337/**
338 * Turn off code scanning
339 *
340 * @returns VBox status code.
341 * @param pVM The cross context VM structure.
342 */
343VMM_INT_DECL(int) CSAMDisableScanning(PVM pVM)
344{
345 pVM->fCSAMEnabled = false;
346 return VINF_SUCCESS;
347}
348
349
350/**
351 * Check if we've scanned this instruction before. If true, then we can emulate
352 * it instead of returning to ring 3.
353 *
354 * Using a simple array here as there are generally few mov crx instructions and
355 * tree lookup is likely to be more expensive. (as it would also have to be offset based)
356 *
357 * @returns boolean
358 * @param pVM The cross context VM structure.
359 * @param GCPtr GC pointer of page table entry
360 */
361VMM_INT_DECL(bool) CSAMIsKnownDangerousInstr(PVM pVM, RTRCUINTPTR GCPtr)
362{
363 Assert(!HMIsEnabled(pVM));
364
365 for (uint32_t i=0;i<pVM->csam.s.cDangerousInstr;i++)
366 {
367 if (pVM->csam.s.aDangerousInstr[i] == (RTRCPTR)GCPtr)
368 {
369 STAM_COUNTER_INC(&pVM->csam.s.StatInstrCacheHit);
370 return true;
371 }
372 }
373 /* Record that we're about to process it in ring 3. */
374 pVM->csam.s.aDangerousInstr[pVM->csam.s.iDangerousInstr++] = (RTRCPTR)GCPtr;
375 pVM->csam.s.iDangerousInstr &= CSAM_MAX_DANGR_INSTR_MASK;
376
377 if (++pVM->csam.s.cDangerousInstr > CSAM_MAX_DANGR_INSTR)
378 pVM->csam.s.cDangerousInstr = CSAM_MAX_DANGR_INSTR;
379
380 STAM_COUNTER_INC(&pVM->csam.s.StatInstrCacheMiss);
381 return false;
382}
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