VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PGMAllHandler.cpp@ 50653

Last change on this file since 50653 was 47786, checked in by vboxsync, 11 years ago

PGM: Added a new page type for the VT-x APIC access page MMIO alias instead of abusing the MMIO2 aliasing. There are important differences, we can safely access the MMIO2 page when aliased and save time doing so, while the alias created by IOMMMIOMapMMIOHCPage must not be accessed outside the VT-x execution AFAIK.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 70.5 KB
Line 
1/* $Id: PGMAllHandler.cpp 47786 2013-08-16 08:59:32Z vboxsync $ */
2/** @file
3 * PGM - Page Manager / Monitor, Access Handlers.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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_PGM
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/pgm.h>
25#include <VBox/vmm/iom.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/em.h>
28#include <VBox/vmm/stam.h>
29#ifdef VBOX_WITH_REM
30# include <VBox/vmm/rem.h>
31#endif
32#include <VBox/vmm/dbgf.h>
33#ifdef VBOX_WITH_REM
34# include <VBox/vmm/rem.h>
35#endif
36#include "PGMInternal.h"
37#include <VBox/vmm/vm.h>
38#include "PGMInline.h"
39
40#include <VBox/log.h>
41#include <iprt/assert.h>
42#include <iprt/asm-amd64-x86.h>
43#include <iprt/string.h>
44#include <VBox/param.h>
45#include <VBox/err.h>
46#include <VBox/vmm/selm.h>
47
48
49/*******************************************************************************
50* Internal Functions *
51*******************************************************************************/
52static int pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(PVM pVM, PPGMPHYSHANDLER pCur, PPGMRAMRANGE pRam);
53static void pgmHandlerPhysicalDeregisterNotifyREM(PVM pVM, PPGMPHYSHANDLER pCur);
54static void pgmHandlerPhysicalResetRamFlags(PVM pVM, PPGMPHYSHANDLER pCur);
55
56
57
58/**
59 * Register a access handler for a physical range.
60 *
61 * @returns VBox status code.
62 * @retval VINF_SUCCESS when successfully installed.
63 * @retval VINF_PGM_GCPHYS_ALIASED when the shadow PTs could be updated because
64 * the guest page aliased or/and mapped by multiple PTs. A CR3 sync has been
65 * flagged together with a pool clearing.
66 * @retval VERR_PGM_HANDLER_PHYSICAL_CONFLICT if the range conflicts with an existing
67 * one. A debug assertion is raised.
68 *
69 * @param pVM Pointer to the VM.
70 * @param enmType Handler type. Any of the PGMPHYSHANDLERTYPE_PHYSICAL* enums.
71 * @param GCPhys Start physical address.
72 * @param GCPhysLast Last physical address. (inclusive)
73 * @param pfnHandlerR3 The R3 handler.
74 * @param pvUserR3 User argument to the R3 handler.
75 * @param pfnHandlerR0 The R0 handler.
76 * @param pvUserR0 User argument to the R0 handler.
77 * @param pfnHandlerRC The RC handler.
78 * @param pvUserRC User argument to the RC handler. This can be a value
79 * less that 0x10000 or a (non-null) pointer that is
80 * automatically relocated.
81 * @param pszDesc Pointer to description string. This must not be freed.
82 */
83VMMDECL(int) PGMHandlerPhysicalRegisterEx(PVM pVM, PGMPHYSHANDLERTYPE enmType, RTGCPHYS GCPhys, RTGCPHYS GCPhysLast,
84 R3PTRTYPE(PFNPGMR3PHYSHANDLER) pfnHandlerR3, RTR3PTR pvUserR3,
85 R0PTRTYPE(PFNPGMR0PHYSHANDLER) pfnHandlerR0, RTR0PTR pvUserR0,
86 RCPTRTYPE(PFNPGMRCPHYSHANDLER) pfnHandlerRC, RTRCPTR pvUserRC,
87 R3PTRTYPE(const char *) pszDesc)
88{
89 Log(("PGMHandlerPhysicalRegisterEx: enmType=%d GCPhys=%RGp GCPhysLast=%RGp pfnHandlerR3=%RHv pvUserR3=%RHv pfnHandlerR0=%RHv pvUserR0=%RHv pfnHandlerGC=%RRv pvUserGC=%RRv pszDesc=%s\n",
90 enmType, GCPhys, GCPhysLast, pfnHandlerR3, pvUserR3, pfnHandlerR0, pvUserR0, pfnHandlerRC, pvUserRC, R3STRING(pszDesc)));
91
92 /*
93 * Validate input.
94 */
95 AssertMsgReturn(GCPhys < GCPhysLast, ("GCPhys >= GCPhysLast (%#x >= %#x)\n", GCPhys, GCPhysLast), VERR_INVALID_PARAMETER);
96 switch (enmType)
97 {
98 case PGMPHYSHANDLERTYPE_PHYSICAL_WRITE:
99 break;
100 case PGMPHYSHANDLERTYPE_MMIO:
101 case PGMPHYSHANDLERTYPE_PHYSICAL_ALL:
102 /* Simplification for PGMPhysRead, PGMR0Trap0eHandlerNPMisconfig and others. */
103 AssertMsgReturn(!(GCPhys & PAGE_OFFSET_MASK), ("%RGp\n", GCPhys), VERR_INVALID_PARAMETER);
104 AssertMsgReturn((GCPhysLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK, ("%RGp\n", GCPhysLast), VERR_INVALID_PARAMETER);
105 break;
106 default:
107 AssertMsgFailed(("Invalid input enmType=%d!\n", enmType));
108 return VERR_INVALID_PARAMETER;
109 }
110 AssertMsgReturn( (RTRCUINTPTR)pvUserRC < 0x10000
111 || MMHyperR3ToRC(pVM, MMHyperRCToR3(pVM, pvUserRC)) == pvUserRC,
112 ("Not RC pointer! pvUserRC=%RRv\n", pvUserRC),
113 VERR_INVALID_PARAMETER);
114 AssertMsgReturn( (RTR0UINTPTR)pvUserR0 < 0x10000
115 || MMHyperR3ToR0(pVM, MMHyperR0ToR3(pVM, pvUserR0)) == pvUserR0,
116 ("Not R0 pointer! pvUserR0=%RHv\n", pvUserR0),
117 VERR_INVALID_PARAMETER);
118 AssertPtrReturn(pfnHandlerR3, VERR_INVALID_POINTER);
119 AssertReturn(pfnHandlerR0, VERR_INVALID_PARAMETER);
120 AssertReturn(pfnHandlerRC || HMIsEnabled(pVM), VERR_INVALID_PARAMETER);
121
122 /*
123 * We require the range to be within registered ram.
124 * There is no apparent need to support ranges which cover more than one ram range.
125 */
126 PPGMRAMRANGE pRam = pgmPhysGetRange(pVM, GCPhys);
127 if ( !pRam
128 || GCPhysLast < pRam->GCPhys
129 || GCPhys > pRam->GCPhysLast)
130 {
131#ifdef IN_RING3
132 DBGFR3Info(pVM->pUVM, "phys", NULL, NULL);
133#endif
134 AssertMsgFailed(("No RAM range for %RGp-%RGp\n", GCPhys, GCPhysLast));
135 return VERR_PGM_HANDLER_PHYSICAL_NO_RAM_RANGE;
136 }
137
138 /*
139 * Allocate and initialize the new entry.
140 */
141 PPGMPHYSHANDLER pNew;
142 int rc = MMHyperAlloc(pVM, sizeof(*pNew), 0, MM_TAG_PGM_HANDLERS, (void **)&pNew);
143 if (RT_FAILURE(rc))
144 return rc;
145
146 pNew->Core.Key = GCPhys;
147 pNew->Core.KeyLast = GCPhysLast;
148 pNew->enmType = enmType;
149 pNew->cPages = (GCPhysLast - (GCPhys & X86_PTE_PAE_PG_MASK) + PAGE_SIZE) >> PAGE_SHIFT;
150 pNew->cAliasedPages = 0;
151 pNew->cTmpOffPages = 0;
152 pNew->pfnHandlerR3 = pfnHandlerR3;
153 pNew->pvUserR3 = pvUserR3;
154 pNew->pfnHandlerR0 = pfnHandlerR0;
155 pNew->pvUserR0 = pvUserR0;
156 pNew->pfnHandlerRC = pfnHandlerRC;
157 pNew->pvUserRC = pvUserRC;
158 pNew->pszDesc = pszDesc;
159
160 pgmLock(pVM);
161
162 /*
163 * Try insert into list.
164 */
165 if (RTAvlroGCPhysInsert(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, &pNew->Core))
166 {
167 rc = pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(pVM, pNew, pRam);
168 if (rc == VINF_PGM_SYNC_CR3)
169 rc = VINF_PGM_GCPHYS_ALIASED;
170 pgmUnlock(pVM);
171#ifdef VBOX_WITH_REM
172# ifndef IN_RING3
173 REMNotifyHandlerPhysicalRegister(pVM, enmType, GCPhys, GCPhysLast - GCPhys + 1, !!pfnHandlerR3);
174# else
175 REMR3NotifyHandlerPhysicalRegister(pVM, enmType, GCPhys, GCPhysLast - GCPhys + 1, !!pfnHandlerR3);
176# endif
177#endif
178 if (rc != VINF_SUCCESS)
179 Log(("PGMHandlerPhysicalRegisterEx: returns %Rrc (%RGp-%RGp)\n", rc, GCPhys, GCPhysLast));
180 return rc;
181 }
182
183 pgmUnlock(pVM);
184
185#if defined(IN_RING3) && defined(VBOX_STRICT)
186 DBGFR3Info(pVM->pUVM, "handlers", "phys nostats", NULL);
187#endif
188 AssertMsgFailed(("Conflict! GCPhys=%RGp GCPhysLast=%RGp pszDesc=%s\n", GCPhys, GCPhysLast, pszDesc));
189 MMHyperFree(pVM, pNew);
190 return VERR_PGM_HANDLER_PHYSICAL_CONFLICT;
191}
192
193
194/**
195 * Sets ram range flags and attempts updating shadow PTs.
196 *
197 * @returns VBox status code.
198 * @retval VINF_SUCCESS when shadow PTs was successfully updated.
199 * @retval VINF_PGM_SYNC_CR3 when the shadow PTs could be updated because
200 * the guest page aliased or/and mapped by multiple PTs. FFs set.
201 * @param pVM Pointer to the VM.
202 * @param pCur The physical handler.
203 * @param pRam The RAM range.
204 */
205static int pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(PVM pVM, PPGMPHYSHANDLER pCur, PPGMRAMRANGE pRam)
206{
207 /*
208 * Iterate the guest ram pages updating the flags and flushing PT entries
209 * mapping the page.
210 */
211 bool fFlushTLBs = false;
212 int rc = VINF_SUCCESS;
213 const unsigned uState = pgmHandlerPhysicalCalcState(pCur);
214 uint32_t cPages = pCur->cPages;
215 uint32_t i = (pCur->Core.Key - pRam->GCPhys) >> PAGE_SHIFT;
216 for (;;)
217 {
218 PPGMPAGE pPage = &pRam->aPages[i];
219 AssertMsg(pCur->enmType != PGMPHYSHANDLERTYPE_MMIO || PGM_PAGE_IS_MMIO(pPage),
220 ("%RGp %R[pgmpage]\n", pRam->GCPhys + (i << PAGE_SHIFT), pPage));
221
222 /* Only do upgrades. */
223 if (PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) < uState)
224 {
225 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, uState);
226
227 int rc2 = pgmPoolTrackUpdateGCPhys(pVM, pRam->GCPhys + (i << PAGE_SHIFT), pPage,
228 false /* allow updates of PTEs (instead of flushing) */, &fFlushTLBs);
229 if (rc2 != VINF_SUCCESS && rc == VINF_SUCCESS)
230 rc = rc2;
231 }
232
233 /* next */
234 if (--cPages == 0)
235 break;
236 i++;
237 }
238
239 if (fFlushTLBs)
240 {
241 PGM_INVL_ALL_VCPU_TLBS(pVM);
242 Log(("pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs: flushing guest TLBs; rc=%d\n", rc));
243 }
244 else
245 Log(("pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs: doesn't flush guest TLBs. rc=%Rrc; sync flags=%x VMCPU_FF_PGM_SYNC_CR3=%d\n", rc, VMMGetCpu(pVM)->pgm.s.fSyncFlags, VMCPU_FF_IS_SET(VMMGetCpu(pVM), VMCPU_FF_PGM_SYNC_CR3)));
246
247 return rc;
248}
249
250
251/**
252 * Register a physical page access handler.
253 *
254 * @returns VBox status code.
255 * @param pVM Pointer to the VM.
256 * @param GCPhys Start physical address.
257 */
258VMMDECL(int) PGMHandlerPhysicalDeregister(PVM pVM, RTGCPHYS GCPhys)
259{
260 /*
261 * Find the handler.
262 */
263 pgmLock(pVM);
264 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysRemove(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
265 if (pCur)
266 {
267 LogFlow(("PGMHandlerPhysicalDeregister: Removing Range %RGp-%RGp %s\n",
268 pCur->Core.Key, pCur->Core.KeyLast, R3STRING(pCur->pszDesc)));
269
270 /*
271 * Clear the page bits, notify the REM about this change and clear
272 * the cache.
273 */
274 pgmHandlerPhysicalResetRamFlags(pVM, pCur);
275 pgmHandlerPhysicalDeregisterNotifyREM(pVM, pCur);
276 pVM->pgm.s.pLastPhysHandlerR0 = 0;
277 pVM->pgm.s.pLastPhysHandlerR3 = 0;
278 pVM->pgm.s.pLastPhysHandlerRC = 0;
279 MMHyperFree(pVM, pCur);
280 pgmUnlock(pVM);
281 return VINF_SUCCESS;
282 }
283 pgmUnlock(pVM);
284
285 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhys));
286 return VERR_PGM_HANDLER_NOT_FOUND;
287}
288
289
290/**
291 * Shared code with modify.
292 */
293static void pgmHandlerPhysicalDeregisterNotifyREM(PVM pVM, PPGMPHYSHANDLER pCur)
294{
295 RTGCPHYS GCPhysStart = pCur->Core.Key;
296 RTGCPHYS GCPhysLast = pCur->Core.KeyLast;
297
298 /*
299 * Page align the range.
300 *
301 * Since we've reset (recalculated) the physical handler state of all pages
302 * we can make use of the page states to figure out whether a page should be
303 * included in the REM notification or not.
304 */
305 if ( (pCur->Core.Key & PAGE_OFFSET_MASK)
306 || ((pCur->Core.KeyLast + 1) & PAGE_OFFSET_MASK))
307 {
308 Assert(pCur->enmType != PGMPHYSHANDLERTYPE_MMIO);
309
310 if (GCPhysStart & PAGE_OFFSET_MASK)
311 {
312 PPGMPAGE pPage = pgmPhysGetPage(pVM, GCPhysStart);
313 if ( pPage
314 && PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) != PGM_PAGE_HNDL_PHYS_STATE_NONE)
315 {
316 RTGCPHYS GCPhys = (GCPhysStart + (PAGE_SIZE - 1)) & X86_PTE_PAE_PG_MASK;
317 if ( GCPhys > GCPhysLast
318 || GCPhys < GCPhysStart)
319 return;
320 GCPhysStart = GCPhys;
321 }
322 else
323 GCPhysStart &= X86_PTE_PAE_PG_MASK;
324 Assert(!pPage || PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO); /* these are page aligned atm! */
325 }
326
327 if (GCPhysLast & PAGE_OFFSET_MASK)
328 {
329 PPGMPAGE pPage = pgmPhysGetPage(pVM, GCPhysLast);
330 if ( pPage
331 && PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) != PGM_PAGE_HNDL_PHYS_STATE_NONE)
332 {
333 RTGCPHYS GCPhys = (GCPhysLast & X86_PTE_PAE_PG_MASK) - 1;
334 if ( GCPhys < GCPhysStart
335 || GCPhys > GCPhysLast)
336 return;
337 GCPhysLast = GCPhys;
338 }
339 else
340 GCPhysLast |= PAGE_OFFSET_MASK;
341 Assert(!pPage || PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO); /* these are page aligned atm! */
342 }
343 }
344
345 /*
346 * Tell REM.
347 */
348 const bool fRestoreAsRAM = pCur->pfnHandlerR3
349 && pCur->enmType != PGMPHYSHANDLERTYPE_MMIO; /** @todo this isn't entirely correct. */
350#ifdef VBOX_WITH_REM
351# ifndef IN_RING3
352 REMNotifyHandlerPhysicalDeregister(pVM, pCur->enmType, GCPhysStart, GCPhysLast - GCPhysStart + 1, !!pCur->pfnHandlerR3, fRestoreAsRAM);
353# else
354 REMR3NotifyHandlerPhysicalDeregister(pVM, pCur->enmType, GCPhysStart, GCPhysLast - GCPhysStart + 1, !!pCur->pfnHandlerR3, fRestoreAsRAM);
355# endif
356#endif
357}
358
359
360/**
361 * pgmHandlerPhysicalResetRamFlags helper that checks for other handlers on
362 * edge pages.
363 */
364DECLINLINE(void) pgmHandlerPhysicalRecalcPageState(PVM pVM, RTGCPHYS GCPhys, bool fAbove, PPGMRAMRANGE *ppRamHint)
365{
366 /*
367 * Look for other handlers.
368 */
369 unsigned uState = PGM_PAGE_HNDL_PHYS_STATE_NONE;
370 for (;;)
371 {
372 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGetBestFit(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys, fAbove);
373 if ( !pCur
374 || ((fAbove ? pCur->Core.Key : pCur->Core.KeyLast) >> PAGE_SHIFT) != (GCPhys >> PAGE_SHIFT))
375 break;
376 unsigned uThisState = pgmHandlerPhysicalCalcState(pCur);
377 uState = RT_MAX(uState, uThisState);
378
379 /* next? */
380 RTGCPHYS GCPhysNext = fAbove
381 ? pCur->Core.KeyLast + 1
382 : pCur->Core.Key - 1;
383 if ((GCPhysNext >> PAGE_SHIFT) != (GCPhys >> PAGE_SHIFT))
384 break;
385 GCPhys = GCPhysNext;
386 }
387
388 /*
389 * Update if we found something that is a higher priority
390 * state than the current.
391 */
392 if (uState != PGM_PAGE_HNDL_PHYS_STATE_NONE)
393 {
394 PPGMPAGE pPage;
395 int rc = pgmPhysGetPageWithHintEx(pVM, GCPhys, &pPage, ppRamHint);
396 if ( RT_SUCCESS(rc)
397 && PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) < uState)
398 {
399 /* This should normally not be necessary. */
400 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, uState);
401 bool fFlushTLBs ;
402 rc = pgmPoolTrackUpdateGCPhys(pVM, GCPhys, pPage, false /*fFlushPTEs*/, &fFlushTLBs);
403 if (RT_SUCCESS(rc) && fFlushTLBs)
404 PGM_INVL_ALL_VCPU_TLBS(pVM);
405 else
406 AssertRC(rc);
407 }
408 else
409 AssertRC(rc);
410 }
411}
412
413
414/**
415 * Resets an aliased page.
416 *
417 * @param pVM The VM.
418 * @param pPage The page.
419 * @param GCPhysPage The page address in case it comes in handy.
420 * @param fDoAccounting Whether to perform accounting. (Only set during
421 * reset where pgmR3PhysRamReset doesn't have the
422 * handler structure handy.)
423 */
424void pgmHandlerPhysicalResetAliasedPage(PVM pVM, PPGMPAGE pPage, RTGCPHYS GCPhysPage, bool fDoAccounting)
425{
426 Assert( PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2_ALIAS_MMIO
427 || PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_SPECIAL_ALIAS_MMIO);
428 Assert(PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) == PGM_PAGE_HNDL_PHYS_STATE_DISABLED);
429
430 /*
431 * Flush any shadow page table references *first*.
432 */
433 bool fFlushTLBs = false;
434 int rc = pgmPoolTrackUpdateGCPhys(pVM, GCPhysPage, pPage, true /*fFlushPTEs*/, &fFlushTLBs);
435 AssertLogRelRCReturnVoid(rc);
436# ifdef IN_RC
437 if (fFlushTLBs && rc != VINF_PGM_SYNC_CR3)
438 PGM_INVL_VCPU_TLBS(VMMGetCpu0(pVM));
439# else
440 HMFlushTLBOnAllVCpus(pVM);
441# endif
442
443 /*
444 * Make it an MMIO/Zero page.
445 */
446 PGM_PAGE_SET_HCPHYS(pVM, pPage, pVM->pgm.s.HCPhysZeroPg);
447 PGM_PAGE_SET_TYPE(pVM, pPage, PGMPAGETYPE_MMIO);
448 PGM_PAGE_SET_STATE(pVM, pPage, PGM_PAGE_STATE_ZERO);
449 PGM_PAGE_SET_PAGEID(pVM, pPage, NIL_GMM_PAGEID);
450 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, PGM_PAGE_HNDL_PHYS_STATE_ALL);
451
452 /* Flush its TLB entry. */
453 pgmPhysInvalidatePageMapTLBEntry(pVM, GCPhysPage);
454
455 /*
456 * Do accounting for pgmR3PhysRamReset.
457 */
458 if (fDoAccounting)
459 {
460 PPGMPHYSHANDLER pHandler = pgmHandlerPhysicalLookup(pVM, GCPhysPage);
461 if (RT_LIKELY(pHandler))
462 {
463 Assert(pHandler->cAliasedPages > 0);
464 pHandler->cAliasedPages--;
465 }
466 else
467 AssertFailed();
468 }
469}
470
471
472/**
473 * Resets ram range flags.
474 *
475 * @returns VBox status code.
476 * @retval VINF_SUCCESS when shadow PTs was successfully updated.
477 * @param pVM Pointer to the VM.
478 * @param pCur The physical handler.
479 *
480 * @remark We don't start messing with the shadow page tables, as we've
481 * already got code in Trap0e which deals with out of sync handler
482 * flags (originally conceived for global pages).
483 */
484static void pgmHandlerPhysicalResetRamFlags(PVM pVM, PPGMPHYSHANDLER pCur)
485{
486 /*
487 * Iterate the guest ram pages updating the state.
488 */
489 RTUINT cPages = pCur->cPages;
490 RTGCPHYS GCPhys = pCur->Core.Key;
491 PPGMRAMRANGE pRamHint = NULL;
492 for (;;)
493 {
494 PPGMPAGE pPage;
495 int rc = pgmPhysGetPageWithHintEx(pVM, GCPhys, &pPage, &pRamHint);
496 if (RT_SUCCESS(rc))
497 {
498 /* Reset aliased MMIO pages to MMIO, since this aliasing is our business.
499 (We don't flip MMIO to RAM though, that's PGMPhys.cpp's job.) */
500 if ( PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2_ALIAS_MMIO
501 || PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_SPECIAL_ALIAS_MMIO)
502 {
503 Assert(pCur->cAliasedPages > 0);
504 pgmHandlerPhysicalResetAliasedPage(pVM, pPage, GCPhys, false /*fDoAccounting*/);
505 pCur->cAliasedPages--;
506 }
507 AssertMsg(pCur->enmType != PGMPHYSHANDLERTYPE_MMIO || PGM_PAGE_IS_MMIO(pPage), ("%RGp %R[pgmpage]\n", GCPhys, pPage));
508 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, PGM_PAGE_HNDL_PHYS_STATE_NONE);
509 }
510 else
511 AssertRC(rc);
512
513 /* next */
514 if (--cPages == 0)
515 break;
516 GCPhys += PAGE_SIZE;
517 }
518
519 pCur->cAliasedPages = 0;
520 pCur->cTmpOffPages = 0;
521
522 /*
523 * Check for partial start and end pages.
524 */
525 if (pCur->Core.Key & PAGE_OFFSET_MASK)
526 pgmHandlerPhysicalRecalcPageState(pVM, pCur->Core.Key - 1, false /* fAbove */, &pRamHint);
527 if ((pCur->Core.KeyLast & PAGE_OFFSET_MASK) != PAGE_OFFSET_MASK)
528 pgmHandlerPhysicalRecalcPageState(pVM, pCur->Core.KeyLast + 1, true /* fAbove */, &pRamHint);
529}
530
531
532/**
533 * Modify a physical page access handler.
534 *
535 * Modification can only be done to the range it self, not the type or anything else.
536 *
537 * @returns VBox status code.
538 * For all return codes other than VERR_PGM_HANDLER_NOT_FOUND and VINF_SUCCESS the range is deregistered
539 * and a new registration must be performed!
540 * @param pVM Pointer to the VM.
541 * @param GCPhysCurrent Current location.
542 * @param GCPhys New location.
543 * @param GCPhysLast New last location.
544 */
545VMMDECL(int) PGMHandlerPhysicalModify(PVM pVM, RTGCPHYS GCPhysCurrent, RTGCPHYS GCPhys, RTGCPHYS GCPhysLast)
546{
547 /*
548 * Remove it.
549 */
550 int rc;
551 pgmLock(pVM);
552 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysRemove(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhysCurrent);
553 if (pCur)
554 {
555 /*
556 * Clear the ram flags. (We're gonna move or free it!)
557 */
558 pgmHandlerPhysicalResetRamFlags(pVM, pCur);
559 const bool fRestoreAsRAM = pCur->pfnHandlerR3
560 && pCur->enmType != PGMPHYSHANDLERTYPE_MMIO; /** @todo this isn't entirely correct. */
561
562 /*
563 * Validate the new range, modify and reinsert.
564 */
565 if (GCPhysLast >= GCPhys)
566 {
567 /*
568 * We require the range to be within registered ram.
569 * There is no apparent need to support ranges which cover more than one ram range.
570 */
571 PPGMRAMRANGE pRam = pgmPhysGetRange(pVM, GCPhys);
572 if ( pRam
573 && GCPhys <= pRam->GCPhysLast
574 && GCPhysLast >= pRam->GCPhys)
575 {
576 pCur->Core.Key = GCPhys;
577 pCur->Core.KeyLast = GCPhysLast;
578 pCur->cPages = (GCPhysLast - (GCPhys & X86_PTE_PAE_PG_MASK) + 1) >> PAGE_SHIFT;
579
580 if (RTAvlroGCPhysInsert(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, &pCur->Core))
581 {
582 PGMPHYSHANDLERTYPE enmType = pCur->enmType;
583 RTGCPHYS cb = GCPhysLast - GCPhys + 1;
584 bool fHasHCHandler = !!pCur->pfnHandlerR3;
585
586 /*
587 * Set ram flags, flush shadow PT entries and finally tell REM about this.
588 */
589 rc = pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(pVM, pCur, pRam);
590 pgmUnlock(pVM);
591
592#ifdef VBOX_WITH_REM
593# ifndef IN_RING3
594 REMNotifyHandlerPhysicalModify(pVM, enmType, GCPhysCurrent, GCPhys, cb,
595 fHasHCHandler, fRestoreAsRAM);
596# else
597 REMR3NotifyHandlerPhysicalModify(pVM, enmType, GCPhysCurrent, GCPhys, cb,
598 fHasHCHandler, fRestoreAsRAM);
599# endif
600#endif
601 PGM_INVL_ALL_VCPU_TLBS(pVM);
602 Log(("PGMHandlerPhysicalModify: GCPhysCurrent=%RGp -> GCPhys=%RGp GCPhysLast=%RGp\n",
603 GCPhysCurrent, GCPhys, GCPhysLast));
604 return VINF_SUCCESS;
605 }
606
607 AssertMsgFailed(("Conflict! GCPhys=%RGp GCPhysLast=%RGp\n", GCPhys, GCPhysLast));
608 rc = VERR_PGM_HANDLER_PHYSICAL_CONFLICT;
609 }
610 else
611 {
612 AssertMsgFailed(("No RAM range for %RGp-%RGp\n", GCPhys, GCPhysLast));
613 rc = VERR_PGM_HANDLER_PHYSICAL_NO_RAM_RANGE;
614 }
615 }
616 else
617 {
618 AssertMsgFailed(("Invalid range %RGp-%RGp\n", GCPhys, GCPhysLast));
619 rc = VERR_INVALID_PARAMETER;
620 }
621
622 /*
623 * Invalid new location, flush the cache and free it.
624 * We've only gotta notify REM and free the memory.
625 */
626 pgmHandlerPhysicalDeregisterNotifyREM(pVM, pCur);
627 pVM->pgm.s.pLastPhysHandlerR0 = 0;
628 pVM->pgm.s.pLastPhysHandlerR3 = 0;
629 pVM->pgm.s.pLastPhysHandlerRC = 0;
630 MMHyperFree(pVM, pCur);
631 }
632 else
633 {
634 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhysCurrent));
635 rc = VERR_PGM_HANDLER_NOT_FOUND;
636 }
637
638 pgmUnlock(pVM);
639 return rc;
640}
641
642
643/**
644 * Changes the callbacks associated with a physical access handler.
645 *
646 * @returns VBox status code.
647 * @param pVM Pointer to the VM.
648 * @param GCPhys Start physical address.
649 * @param pfnHandlerR3 The R3 handler.
650 * @param pvUserR3 User argument to the R3 handler.
651 * @param pfnHandlerR0 The R0 handler.
652 * @param pvUserR0 User argument to the R0 handler.
653 * @param pfnHandlerRC The RC handler.
654 * @param pvUserRC User argument to the RC handler. Values larger or
655 * equal to 0x10000 will be relocated automatically.
656 * @param pszDesc Pointer to description string. This must not be freed.
657 */
658VMMDECL(int) PGMHandlerPhysicalChangeCallbacks(PVM pVM, RTGCPHYS GCPhys,
659 R3PTRTYPE(PFNPGMR3PHYSHANDLER) pfnHandlerR3, RTR3PTR pvUserR3,
660 R0PTRTYPE(PFNPGMR0PHYSHANDLER) pfnHandlerR0, RTR0PTR pvUserR0,
661 RCPTRTYPE(PFNPGMRCPHYSHANDLER) pfnHandlerRC, RTRCPTR pvUserRC,
662 R3PTRTYPE(const char *) pszDesc)
663{
664 /*
665 * Get the handler.
666 */
667 int rc = VINF_SUCCESS;
668 pgmLock(pVM);
669 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
670 if (pCur)
671 {
672 /*
673 * Change callbacks.
674 */
675 pCur->pfnHandlerR3 = pfnHandlerR3;
676 pCur->pvUserR3 = pvUserR3;
677 pCur->pfnHandlerR0 = pfnHandlerR0;
678 pCur->pvUserR0 = pvUserR0;
679 pCur->pfnHandlerRC = pfnHandlerRC;
680 pCur->pvUserRC = pvUserRC;
681 pCur->pszDesc = pszDesc;
682 }
683 else
684 {
685 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhys));
686 rc = VERR_PGM_HANDLER_NOT_FOUND;
687 }
688
689 pgmUnlock(pVM);
690 return rc;
691}
692
693
694/**
695 * Splits a physical access handler in two.
696 *
697 * @returns VBox status code.
698 * @param pVM Pointer to the VM.
699 * @param GCPhys Start physical address of the handler.
700 * @param GCPhysSplit The split address.
701 */
702VMMDECL(int) PGMHandlerPhysicalSplit(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysSplit)
703{
704 AssertReturn(GCPhys < GCPhysSplit, VERR_INVALID_PARAMETER);
705
706 /*
707 * Do the allocation without owning the lock.
708 */
709 PPGMPHYSHANDLER pNew;
710 int rc = MMHyperAlloc(pVM, sizeof(*pNew), 0, MM_TAG_PGM_HANDLERS, (void **)&pNew);
711 if (RT_FAILURE(rc))
712 return rc;
713
714 /*
715 * Get the handler.
716 */
717 pgmLock(pVM);
718 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
719 if (RT_LIKELY(pCur))
720 {
721 if (RT_LIKELY(GCPhysSplit <= pCur->Core.KeyLast))
722 {
723 /*
724 * Create new handler node for the 2nd half.
725 */
726 *pNew = *pCur;
727 pNew->Core.Key = GCPhysSplit;
728 pNew->cPages = (pNew->Core.KeyLast - (pNew->Core.Key & X86_PTE_PAE_PG_MASK) + PAGE_SIZE) >> PAGE_SHIFT;
729
730 pCur->Core.KeyLast = GCPhysSplit - 1;
731 pCur->cPages = (pCur->Core.KeyLast - (pCur->Core.Key & X86_PTE_PAE_PG_MASK) + PAGE_SIZE) >> PAGE_SHIFT;
732
733 if (RT_LIKELY(RTAvlroGCPhysInsert(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, &pNew->Core)))
734 {
735 LogFlow(("PGMHandlerPhysicalSplit: %RGp-%RGp and %RGp-%RGp\n",
736 pCur->Core.Key, pCur->Core.KeyLast, pNew->Core.Key, pNew->Core.KeyLast));
737 pgmUnlock(pVM);
738 return VINF_SUCCESS;
739 }
740 AssertMsgFailed(("whu?\n"));
741 rc = VERR_PGM_PHYS_HANDLER_IPE;
742 }
743 else
744 {
745 AssertMsgFailed(("outside range: %RGp-%RGp split %RGp\n", pCur->Core.Key, pCur->Core.KeyLast, GCPhysSplit));
746 rc = VERR_INVALID_PARAMETER;
747 }
748 }
749 else
750 {
751 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhys));
752 rc = VERR_PGM_HANDLER_NOT_FOUND;
753 }
754 pgmUnlock(pVM);
755 MMHyperFree(pVM, pNew);
756 return rc;
757}
758
759
760/**
761 * Joins up two adjacent physical access handlers which has the same callbacks.
762 *
763 * @returns VBox status code.
764 * @param pVM Pointer to the VM.
765 * @param GCPhys1 Start physical address of the first handler.
766 * @param GCPhys2 Start physical address of the second handler.
767 */
768VMMDECL(int) PGMHandlerPhysicalJoin(PVM pVM, RTGCPHYS GCPhys1, RTGCPHYS GCPhys2)
769{
770 /*
771 * Get the handlers.
772 */
773 int rc;
774 pgmLock(pVM);
775 PPGMPHYSHANDLER pCur1 = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys1);
776 if (RT_LIKELY(pCur1))
777 {
778 PPGMPHYSHANDLER pCur2 = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys2);
779 if (RT_LIKELY(pCur2))
780 {
781 /*
782 * Make sure that they are adjacent, and that they've got the same callbacks.
783 */
784 if (RT_LIKELY(pCur1->Core.KeyLast + 1 == pCur2->Core.Key))
785 {
786 if (RT_LIKELY( pCur1->pfnHandlerRC == pCur2->pfnHandlerRC
787 && pCur1->pfnHandlerR0 == pCur2->pfnHandlerR0
788 && pCur1->pfnHandlerR3 == pCur2->pfnHandlerR3))
789 {
790 PPGMPHYSHANDLER pCur3 = (PPGMPHYSHANDLER)RTAvlroGCPhysRemove(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys2);
791 if (RT_LIKELY(pCur3 == pCur2))
792 {
793 pCur1->Core.KeyLast = pCur2->Core.KeyLast;
794 pCur1->cPages = (pCur1->Core.KeyLast - (pCur1->Core.Key & X86_PTE_PAE_PG_MASK) + PAGE_SIZE) >> PAGE_SHIFT;
795 LogFlow(("PGMHandlerPhysicalJoin: %RGp-%RGp %RGp-%RGp\n",
796 pCur1->Core.Key, pCur1->Core.KeyLast, pCur2->Core.Key, pCur2->Core.KeyLast));
797 pVM->pgm.s.pLastPhysHandlerR0 = 0;
798 pVM->pgm.s.pLastPhysHandlerR3 = 0;
799 pVM->pgm.s.pLastPhysHandlerRC = 0;
800 MMHyperFree(pVM, pCur2);
801 pgmUnlock(pVM);
802 return VINF_SUCCESS;
803 }
804
805 Assert(pCur3 == pCur2);
806 rc = VERR_PGM_PHYS_HANDLER_IPE;
807 }
808 else
809 {
810 AssertMsgFailed(("mismatching handlers\n"));
811 rc = VERR_ACCESS_DENIED;
812 }
813 }
814 else
815 {
816 AssertMsgFailed(("not adjacent: %RGp-%RGp %RGp-%RGp\n",
817 pCur1->Core.Key, pCur1->Core.KeyLast, pCur2->Core.Key, pCur2->Core.KeyLast));
818 rc = VERR_INVALID_PARAMETER;
819 }
820 }
821 else
822 {
823 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhys2));
824 rc = VERR_PGM_HANDLER_NOT_FOUND;
825 }
826 }
827 else
828 {
829 AssertMsgFailed(("Didn't find range starting at %RGp\n", GCPhys1));
830 rc = VERR_PGM_HANDLER_NOT_FOUND;
831 }
832 pgmUnlock(pVM);
833 return rc;
834
835}
836
837
838/**
839 * Resets any modifications to individual pages in a physical page access
840 * handler region.
841 *
842 * This is used in pair with PGMHandlerPhysicalPageTempOff(),
843 * PGMHandlerPhysicalPageAlias() or PGMHandlerPhysicalPageAliasHC().
844 *
845 * @returns VBox status code.
846 * @param pVM Pointer to the VM
847 * @param GCPhys The start address of the handler regions, i.e. what you
848 * passed to PGMR3HandlerPhysicalRegister(),
849 * PGMHandlerPhysicalRegisterEx() or
850 * PGMHandlerPhysicalModify().
851 */
852VMMDECL(int) PGMHandlerPhysicalReset(PVM pVM, RTGCPHYS GCPhys)
853{
854 LogFlow(("PGMHandlerPhysicalReset GCPhys=%RGp\n", GCPhys));
855 pgmLock(pVM);
856
857 /*
858 * Find the handler.
859 */
860 int rc;
861 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
862 if (RT_LIKELY(pCur))
863 {
864 /*
865 * Validate type.
866 */
867 switch (pCur->enmType)
868 {
869 case PGMPHYSHANDLERTYPE_PHYSICAL_WRITE:
870 case PGMPHYSHANDLERTYPE_PHYSICAL_ALL:
871 case PGMPHYSHANDLERTYPE_MMIO: /* NOTE: Only use when clearing MMIO ranges with aliased MMIO2 pages! */
872 {
873 STAM_COUNTER_INC(&pVM->pgm.s.CTX_SUFF(pStats)->CTX_MID_Z(Stat,PhysHandlerReset)); /**@Todo move out of switch */
874 PPGMRAMRANGE pRam = pgmPhysGetRange(pVM, GCPhys);
875 Assert(pRam);
876 Assert(pRam->GCPhys <= pCur->Core.Key);
877 Assert(pRam->GCPhysLast >= pCur->Core.KeyLast);
878
879 if (pCur->enmType == PGMPHYSHANDLERTYPE_MMIO)
880 {
881 /*
882 * Reset all the PGMPAGETYPE_MMIO2_ALIAS_MMIO pages first and that's it.
883 * This could probably be optimized a bit wrt to flushing, but I'm too lazy
884 * to do that now...
885 */
886 if (pCur->cAliasedPages)
887 {
888 PPGMPAGE pPage = &pRam->aPages[(pCur->Core.Key - pRam->GCPhys) >> PAGE_SHIFT];
889 uint32_t cLeft = pCur->cPages;
890 while (cLeft-- > 0)
891 {
892 if ( PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2_ALIAS_MMIO
893 || PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_SPECIAL_ALIAS_MMIO)
894 {
895 Assert(pCur->cAliasedPages > 0);
896 pgmHandlerPhysicalResetAliasedPage(pVM, pPage, pRam->GCPhys + ((RTGCPHYS)cLeft << PAGE_SHIFT),
897 false /*fDoAccounting*/);
898 --pCur->cAliasedPages;
899#ifndef VBOX_STRICT
900 if (pCur->cAliasedPages == 0)
901 break;
902#endif
903 }
904 Assert(PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO);
905 pPage++;
906 }
907 Assert(pCur->cAliasedPages == 0);
908 }
909 }
910 else if (pCur->cTmpOffPages > 0)
911 {
912 /*
913 * Set the flags and flush shadow PT entries.
914 */
915 rc = pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(pVM, pCur, pRam);
916 }
917
918 pCur->cAliasedPages = 0;
919 pCur->cTmpOffPages = 0;
920
921 rc = VINF_SUCCESS;
922 break;
923 }
924
925 /*
926 * Invalid.
927 */
928 default:
929 AssertMsgFailed(("Invalid type %d! Corruption!\n", pCur->enmType));
930 rc = VERR_PGM_PHYS_HANDLER_IPE;
931 break;
932 }
933 }
934 else
935 {
936 AssertMsgFailed(("Didn't find MMIO Range starting at %#x\n", GCPhys));
937 rc = VERR_PGM_HANDLER_NOT_FOUND;
938 }
939
940 pgmUnlock(pVM);
941 return rc;
942}
943
944
945/**
946 * Temporarily turns off the access monitoring of a page within a monitored
947 * physical write/all page access handler region.
948 *
949 * Use this when no further \#PFs are required for that page. Be aware that
950 * a page directory sync might reset the flags, and turn on access monitoring
951 * for the page.
952 *
953 * The caller must do required page table modifications.
954 *
955 * @returns VBox status code.
956 * @param pVM Pointer to the VM
957 * @param GCPhys The start address of the access handler. This
958 * must be a fully page aligned range or we risk
959 * messing up other handlers installed for the
960 * start and end pages.
961 * @param GCPhysPage The physical address of the page to turn off
962 * access monitoring for.
963 */
964VMMDECL(int) PGMHandlerPhysicalPageTempOff(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysPage)
965{
966 LogFlow(("PGMHandlerPhysicalPageTempOff GCPhysPage=%RGp\n", GCPhysPage));
967
968 pgmLock(pVM);
969 /*
970 * Validate the range.
971 */
972 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
973 if (RT_LIKELY(pCur))
974 {
975 if (RT_LIKELY( GCPhysPage >= pCur->Core.Key
976 && GCPhysPage <= pCur->Core.KeyLast))
977 {
978 Assert(!(pCur->Core.Key & PAGE_OFFSET_MASK));
979 Assert((pCur->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK);
980
981 AssertReturnStmt( pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE
982 || pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_ALL,
983 pgmUnlock(pVM), VERR_ACCESS_DENIED);
984
985 /*
986 * Change the page status.
987 */
988 PPGMPAGE pPage;
989 int rc = pgmPhysGetPageEx(pVM, GCPhysPage, &pPage);
990 AssertReturnStmt(RT_SUCCESS_NP(rc), pgmUnlock(pVM), rc);
991 if (PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) != PGM_PAGE_HNDL_PHYS_STATE_DISABLED)
992 {
993 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, PGM_PAGE_HNDL_PHYS_STATE_DISABLED);
994 pCur->cTmpOffPages++;
995 }
996 pgmUnlock(pVM);
997 return VINF_SUCCESS;
998 }
999 pgmUnlock(pVM);
1000 AssertMsgFailed(("The page %#x is outside the range %#x-%#x\n",
1001 GCPhysPage, pCur->Core.Key, pCur->Core.KeyLast));
1002 return VERR_INVALID_PARAMETER;
1003 }
1004 pgmUnlock(pVM);
1005 AssertMsgFailed(("Specified physical handler start address %#x is invalid.\n", GCPhys));
1006 return VERR_PGM_HANDLER_NOT_FOUND;
1007}
1008
1009#ifndef IEM_VERIFICATION_MODE_FULL
1010
1011/**
1012 * Replaces an MMIO page with an MMIO2 page.
1013 *
1014 * This is a worker for IOMMMIOMapMMIO2Page that works in a similar way to
1015 * PGMHandlerPhysicalPageTempOff but for an MMIO page. Since an MMIO page has no
1016 * backing, the caller must provide a replacement page. For various reasons the
1017 * replacement page must be an MMIO2 page.
1018 *
1019 * The caller must do required page table modifications. You can get away
1020 * without making any modifications since it's an MMIO page, the cost is an extra
1021 * \#PF which will the resync the page.
1022 *
1023 * Call PGMHandlerPhysicalReset() to restore the MMIO page.
1024 *
1025 * The caller may still get handler callback even after this call and must be
1026 * able to deal correctly with such calls. The reason for these callbacks are
1027 * either that we're executing in the recompiler (which doesn't know about this
1028 * arrangement) or that we've been restored from saved state (where we won't
1029 * save the change).
1030 *
1031 * @returns VBox status code.
1032 * @param pVM Pointer to the VM.
1033 * @param GCPhys The start address of the access handler. This
1034 * must be a fully page aligned range or we risk
1035 * messing up other handlers installed for the
1036 * start and end pages.
1037 * @param GCPhysPage The physical address of the page to turn off
1038 * access monitoring for.
1039 * @param GCPhysPageRemap The physical address of the MMIO2 page that
1040 * serves as backing memory.
1041 *
1042 * @remark May cause a page pool flush if used on a page that is already
1043 * aliased.
1044 *
1045 * @note This trick does only work reliably if the two pages are never ever
1046 * mapped in the same page table. If they are the page pool code will
1047 * be confused should either of them be flushed. See the special case
1048 * of zero page aliasing mentioned in #3170.
1049 *
1050 */
1051VMMDECL(int) PGMHandlerPhysicalPageAlias(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysPage, RTGCPHYS GCPhysPageRemap)
1052{
1053/// Assert(!IOMIsLockOwner(pVM)); /* We mustn't own any other locks when calling this */
1054
1055 pgmLock(pVM);
1056 /*
1057 * Lookup and validate the range.
1058 */
1059 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
1060 if (RT_LIKELY(pCur))
1061 {
1062 if (RT_LIKELY( GCPhysPage >= pCur->Core.Key
1063 && GCPhysPage <= pCur->Core.KeyLast))
1064 {
1065 AssertReturnStmt(pCur->enmType == PGMPHYSHANDLERTYPE_MMIO, pgmUnlock(pVM), VERR_ACCESS_DENIED);
1066 AssertReturnStmt(!(pCur->Core.Key & PAGE_OFFSET_MASK), pgmUnlock(pVM), VERR_INVALID_PARAMETER);
1067 AssertReturnStmt((pCur->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK, pgmUnlock(pVM), VERR_INVALID_PARAMETER);
1068
1069 /*
1070 * Get and validate the two pages.
1071 */
1072 PPGMPAGE pPageRemap;
1073 int rc = pgmPhysGetPageEx(pVM, GCPhysPageRemap, &pPageRemap);
1074 AssertReturnStmt(RT_SUCCESS_NP(rc), pgmUnlock(pVM), rc);
1075 AssertMsgReturnStmt(PGM_PAGE_GET_TYPE(pPageRemap) == PGMPAGETYPE_MMIO2,
1076 ("GCPhysPageRemap=%RGp %R[pgmpage]\n", GCPhysPageRemap, pPageRemap),
1077 pgmUnlock(pVM), VERR_PGM_PHYS_NOT_MMIO2);
1078
1079 PPGMPAGE pPage;
1080 rc = pgmPhysGetPageEx(pVM, GCPhysPage, &pPage);
1081 AssertReturnStmt(RT_SUCCESS_NP(rc), pgmUnlock(pVM), rc);
1082 if (PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO)
1083 {
1084 AssertMsgReturn(PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_MMIO2_ALIAS_MMIO,
1085 ("GCPhysPage=%RGp %R[pgmpage]\n", GCPhysPage, pPage),
1086 VERR_PGM_PHYS_NOT_MMIO2);
1087 if (PGM_PAGE_GET_HCPHYS(pPage) == PGM_PAGE_GET_HCPHYS(pPageRemap))
1088 {
1089 pgmUnlock(pVM);
1090 return VINF_PGM_HANDLER_ALREADY_ALIASED;
1091 }
1092
1093 /*
1094 * The page is already mapped as some other page, reset it
1095 * to an MMIO/ZERO page before doing the new mapping.
1096 */
1097 Log(("PGMHandlerPhysicalPageAlias: GCPhysPage=%RGp (%R[pgmpage]; %RHp -> %RHp\n",
1098 GCPhysPage, pPage, PGM_PAGE_GET_HCPHYS(pPage), PGM_PAGE_GET_HCPHYS(pPageRemap)));
1099 pgmHandlerPhysicalResetAliasedPage(pVM, pPage, GCPhysPage, false /*fDoAccounting*/);
1100 pCur->cAliasedPages--;
1101 }
1102 Assert(PGM_PAGE_IS_ZERO(pPage));
1103
1104 /*
1105 * Do the actual remapping here.
1106 * This page now serves as an alias for the backing memory specified.
1107 */
1108 LogFlow(("PGMHandlerPhysicalPageAlias: %RGp (%R[pgmpage]) alias for %RGp (%R[pgmpage])\n",
1109 GCPhysPage, pPage, GCPhysPageRemap, pPageRemap ));
1110 PGM_PAGE_SET_HCPHYS(pVM, pPage, PGM_PAGE_GET_HCPHYS(pPageRemap));
1111 PGM_PAGE_SET_TYPE(pVM, pPage, PGMPAGETYPE_MMIO2_ALIAS_MMIO);
1112 PGM_PAGE_SET_STATE(pVM, pPage, PGM_PAGE_STATE_ALLOCATED);
1113 PGM_PAGE_SET_PAGEID(pVM, pPage, PGM_PAGE_GET_PAGEID(pPageRemap));
1114 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, PGM_PAGE_HNDL_PHYS_STATE_DISABLED);
1115 pCur->cAliasedPages++;
1116 Assert(pCur->cAliasedPages <= pCur->cPages);
1117
1118 /* Flush its TLB entry. */
1119 pgmPhysInvalidatePageMapTLBEntry(pVM, GCPhysPage);
1120
1121 LogFlow(("PGMHandlerPhysicalPageAlias: => %R[pgmpage]\n", pPage));
1122 pgmUnlock(pVM);
1123 return VINF_SUCCESS;
1124 }
1125
1126 pgmUnlock(pVM);
1127 AssertMsgFailed(("The page %#x is outside the range %#x-%#x\n",
1128 GCPhysPage, pCur->Core.Key, pCur->Core.KeyLast));
1129 return VERR_INVALID_PARAMETER;
1130 }
1131
1132 pgmUnlock(pVM);
1133 AssertMsgFailed(("Specified physical handler start address %#x is invalid.\n", GCPhys));
1134 return VERR_PGM_HANDLER_NOT_FOUND;
1135}
1136
1137
1138/**
1139 * Replaces an MMIO page with an arbitrary HC page in the shadow page tables.
1140 *
1141 * This differs from PGMHandlerPhysicalPageAlias in that the page doesn't need
1142 * to be a known MMIO2 page and that only shadow paging may access the page.
1143 * The latter distinction is important because the only use for this feature is
1144 * for mapping the special APIC access page that VT-x uses to detect APIC MMIO
1145 * operations, the page is shared between all guest CPUs and actually not
1146 * written to. At least at the moment.
1147 *
1148 * The caller must do required page table modifications. You can get away
1149 * without making any modifications since it's an MMIO page, the cost is an extra
1150 * \#PF which will the resync the page.
1151 *
1152 * Call PGMHandlerPhysicalReset() to restore the MMIO page.
1153 *
1154 *
1155 * @returns VBox status code.
1156 * @param pVM Pointer to the VM.
1157 * @param GCPhys The start address of the access handler. This
1158 * must be a fully page aligned range or we risk
1159 * messing up other handlers installed for the
1160 * start and end pages.
1161 * @param GCPhysPage The physical address of the page to turn off
1162 * access monitoring for.
1163 * @param HCPhysPageRemap The physical address of the HC page that
1164 * serves as backing memory.
1165 *
1166 * @remark May cause a page pool flush if used on a page that is already
1167 * aliased.
1168 */
1169VMMDECL(int) PGMHandlerPhysicalPageAliasHC(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysPage, RTHCPHYS HCPhysPageRemap)
1170{
1171/// Assert(!IOMIsLockOwner(pVM)); /* We mustn't own any other locks when calling this */
1172
1173 /*
1174 * Lookup and validate the range.
1175 */
1176 pgmLock(pVM);
1177 PPGMPHYSHANDLER pCur = (PPGMPHYSHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysHandlers, GCPhys);
1178 if (RT_LIKELY(pCur))
1179 {
1180 if (RT_LIKELY( GCPhysPage >= pCur->Core.Key
1181 && GCPhysPage <= pCur->Core.KeyLast))
1182 {
1183 AssertReturnStmt(pCur->enmType == PGMPHYSHANDLERTYPE_MMIO, pgmUnlock(pVM), VERR_ACCESS_DENIED);
1184 AssertReturnStmt(!(pCur->Core.Key & PAGE_OFFSET_MASK), pgmUnlock(pVM), VERR_INVALID_PARAMETER);
1185 AssertReturnStmt((pCur->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK, pgmUnlock(pVM), VERR_INVALID_PARAMETER);
1186
1187 /*
1188 * Get and validate the pages.
1189 */
1190 PPGMPAGE pPage;
1191 int rc = pgmPhysGetPageEx(pVM, GCPhysPage, &pPage);
1192 AssertReturnStmt(RT_SUCCESS_NP(rc), pgmUnlock(pVM), rc);
1193 if (PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_MMIO)
1194 {
1195 pgmUnlock(pVM);
1196 AssertMsgReturn(PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_SPECIAL_ALIAS_MMIO,
1197 ("GCPhysPage=%RGp %R[pgmpage]\n", GCPhysPage, pPage),
1198 VERR_PGM_PHYS_NOT_MMIO2);
1199 return VINF_PGM_HANDLER_ALREADY_ALIASED;
1200 }
1201 Assert(PGM_PAGE_IS_ZERO(pPage));
1202
1203 /*
1204 * Do the actual remapping here.
1205 * This page now serves as an alias for the backing memory
1206 * specified as far as shadow paging is concerned.
1207 */
1208 LogFlow(("PGMHandlerPhysicalPageAlias: %RGp (%R[pgmpage]) alias for %RHp\n",
1209 GCPhysPage, pPage, HCPhysPageRemap));
1210 PGM_PAGE_SET_HCPHYS(pVM, pPage, HCPhysPageRemap);
1211 PGM_PAGE_SET_TYPE(pVM, pPage, PGMPAGETYPE_SPECIAL_ALIAS_MMIO);
1212 PGM_PAGE_SET_STATE(pVM, pPage, PGM_PAGE_STATE_ALLOCATED);
1213 PGM_PAGE_SET_PAGEID(pVM, pPage, NIL_GMM_PAGEID);
1214 PGM_PAGE_SET_HNDL_PHYS_STATE(pPage, PGM_PAGE_HNDL_PHYS_STATE_DISABLED);
1215 pCur->cAliasedPages++;
1216 Assert(pCur->cAliasedPages <= pCur->cPages);
1217
1218 /* Flush its TLB entry. */
1219 pgmPhysInvalidatePageMapTLBEntry(pVM, GCPhysPage);
1220
1221 LogFlow(("PGMHandlerPhysicalPageAliasHC: => %R[pgmpage]\n", pPage));
1222 pgmUnlock(pVM);
1223 return VINF_SUCCESS;
1224 }
1225 pgmUnlock(pVM);
1226 AssertMsgFailed(("The page %#x is outside the range %#x-%#x\n",
1227 GCPhysPage, pCur->Core.Key, pCur->Core.KeyLast));
1228 return VERR_INVALID_PARAMETER;
1229 }
1230 pgmUnlock(pVM);
1231
1232 AssertMsgFailed(("Specified physical handler start address %#x is invalid.\n", GCPhys));
1233 return VERR_PGM_HANDLER_NOT_FOUND;
1234}
1235
1236#endif /* !IEM_VERIFICATION_MODE_FULL */
1237
1238/**
1239 * Checks if a physical range is handled
1240 *
1241 * @returns boolean
1242 * @param pVM Pointer to the VM.
1243 * @param GCPhys Start physical address earlier passed to PGMR3HandlerPhysicalRegister().
1244 * @remarks Caller must take the PGM lock...
1245 * @thread EMT.
1246 */
1247VMMDECL(bool) PGMHandlerPhysicalIsRegistered(PVM pVM, RTGCPHYS GCPhys)
1248{
1249 /*
1250 * Find the handler.
1251 */
1252 pgmLock(pVM);
1253 PPGMPHYSHANDLER pCur = pgmHandlerPhysicalLookup(pVM, GCPhys);
1254 if (pCur)
1255 {
1256 Assert(GCPhys >= pCur->Core.Key && GCPhys <= pCur->Core.KeyLast);
1257 Assert( pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE
1258 || pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_ALL
1259 || pCur->enmType == PGMPHYSHANDLERTYPE_MMIO);
1260 pgmUnlock(pVM);
1261 return true;
1262 }
1263 pgmUnlock(pVM);
1264 return false;
1265}
1266
1267
1268/**
1269 * Checks if it's an disabled all access handler or write access handler at the
1270 * given address.
1271 *
1272 * @returns true if it's an all access handler, false if it's a write access
1273 * handler.
1274 * @param pVM Pointer to the VM.
1275 * @param GCPhys The address of the page with a disabled handler.
1276 *
1277 * @remarks The caller, PGMR3PhysTlbGCPhys2Ptr, must hold the PGM lock.
1278 */
1279bool pgmHandlerPhysicalIsAll(PVM pVM, RTGCPHYS GCPhys)
1280{
1281 pgmLock(pVM);
1282 PPGMPHYSHANDLER pCur = pgmHandlerPhysicalLookup(pVM, GCPhys);
1283 if (!pCur)
1284 {
1285 pgmUnlock(pVM);
1286 AssertFailed();
1287 return true;
1288 }
1289 Assert( pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_WRITE
1290 || pCur->enmType == PGMPHYSHANDLERTYPE_PHYSICAL_ALL
1291 || pCur->enmType == PGMPHYSHANDLERTYPE_MMIO); /* sanity */
1292 /* Only whole pages can be disabled. */
1293 Assert( pCur->Core.Key <= (GCPhys & ~(RTGCPHYS)PAGE_OFFSET_MASK)
1294 && pCur->Core.KeyLast >= (GCPhys | PAGE_OFFSET_MASK));
1295
1296 bool bRet = pCur->enmType != PGMPHYSHANDLERTYPE_PHYSICAL_WRITE;
1297 pgmUnlock(pVM);
1298 return bRet;
1299}
1300
1301
1302/**
1303 * Check if particular guest's VA is being monitored.
1304 *
1305 * @returns true or false
1306 * @param pVM Pointer to the VM.
1307 * @param GCPtr Virtual address.
1308 * @remarks Will acquire the PGM lock.
1309 * @thread Any.
1310 */
1311VMMDECL(bool) PGMHandlerVirtualIsRegistered(PVM pVM, RTGCPTR GCPtr)
1312{
1313 pgmLock(pVM);
1314 PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)RTAvlroGCPtrGet(&pVM->pgm.s.CTX_SUFF(pTrees)->VirtHandlers, GCPtr);
1315 pgmUnlock(pVM);
1316
1317 return pCur != NULL;
1318}
1319
1320
1321/**
1322 * Search for virtual handler with matching physical address
1323 *
1324 * @returns VBox status code
1325 * @param pVM Pointer to the VM.
1326 * @param GCPhys GC physical address to search for.
1327 * @param ppVirt Where to store the pointer to the virtual handler structure.
1328 * @param piPage Where to store the pointer to the index of the cached physical page.
1329 */
1330int pgmHandlerVirtualFindByPhysAddr(PVM pVM, RTGCPHYS GCPhys, PPGMVIRTHANDLER *ppVirt, unsigned *piPage)
1331{
1332 STAM_PROFILE_START(&pVM->pgm.s.CTX_MID_Z(Stat,VirtHandlerSearchByPhys), a);
1333 Assert(ppVirt);
1334
1335 pgmLock(pVM);
1336 PPGMPHYS2VIRTHANDLER pCur;
1337 pCur = (PPGMPHYS2VIRTHANDLER)RTAvlroGCPhysRangeGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers, GCPhys);
1338 if (pCur)
1339 {
1340 /* found a match! */
1341 *ppVirt = (PPGMVIRTHANDLER)((uintptr_t)pCur + pCur->offVirtHandler);
1342 *piPage = pCur - &(*ppVirt)->aPhysToVirt[0];
1343 pgmUnlock(pVM);
1344
1345#ifdef VBOX_STRICT_PGM_HANDLER_VIRTUAL
1346 AssertRelease(pCur->offNextAlias & PGMPHYS2VIRTHANDLER_IS_HEAD);
1347#endif
1348 LogFlow(("PHYS2VIRT: found match for %RGp -> %RGv *piPage=%#x\n", GCPhys, (*ppVirt)->Core.Key, *piPage));
1349 STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->CTX_MID_Z(Stat,VirtHandlerSearchByPhys), a);
1350 return VINF_SUCCESS;
1351 }
1352
1353 pgmUnlock(pVM);
1354 *ppVirt = NULL;
1355 STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pStats)->CTX_MID_Z(Stat,VirtHandlerSearchByPhys), a);
1356 return VERR_PGM_HANDLER_NOT_FOUND;
1357}
1358
1359
1360/**
1361 * Deal with aliases in phys2virt.
1362 *
1363 * As pointed out by the various todos, this currently only deals with
1364 * aliases where the two ranges match 100%.
1365 *
1366 * @param pVM Pointer to the VM.
1367 * @param pPhys2Virt The node we failed insert.
1368 */
1369static void pgmHandlerVirtualInsertAliased(PVM pVM, PPGMPHYS2VIRTHANDLER pPhys2Virt)
1370{
1371 /*
1372 * First find the node which is conflicting with us.
1373 */
1374 /** @todo Deal with partial overlapping. (Unlikely situation, so I'm too lazy to do anything about it now.) */
1375 /** @todo check if the current head node covers the ground we do. This is highly unlikely
1376 * and I'm too lazy to implement this now as it will require sorting the list and stuff like that. */
1377 PPGMPHYS2VIRTHANDLER pHead = (PPGMPHYS2VIRTHANDLER)RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers, pPhys2Virt->Core.Key);
1378#ifdef VBOX_STRICT_PGM_HANDLER_VIRTUAL
1379 AssertReleaseMsg(pHead != pPhys2Virt, ("%RGp-%RGp offVirtHandler=%#RX32\n",
1380 pPhys2Virt->Core.Key, pPhys2Virt->Core.KeyLast, pPhys2Virt->offVirtHandler));
1381#endif
1382 if (RT_UNLIKELY(!pHead || pHead->Core.KeyLast != pPhys2Virt->Core.KeyLast))
1383 {
1384 /** @todo do something clever here... */
1385 LogRel(("pgmHandlerVirtualInsertAliased: %RGp-%RGp\n", pPhys2Virt->Core.Key, pPhys2Virt->Core.KeyLast));
1386 pPhys2Virt->offNextAlias = 0;
1387 return;
1388 }
1389
1390 /*
1391 * Insert ourselves as the next node.
1392 */
1393 if (!(pHead->offNextAlias & PGMPHYS2VIRTHANDLER_OFF_MASK))
1394 pPhys2Virt->offNextAlias = PGMPHYS2VIRTHANDLER_IN_TREE;
1395 else
1396 {
1397 PPGMPHYS2VIRTHANDLER pNext = (PPGMPHYS2VIRTHANDLER)((intptr_t)pHead + (pHead->offNextAlias & PGMPHYS2VIRTHANDLER_OFF_MASK));
1398 pPhys2Virt->offNextAlias = ((intptr_t)pNext - (intptr_t)pPhys2Virt)
1399 | PGMPHYS2VIRTHANDLER_IN_TREE;
1400 }
1401 pHead->offNextAlias = ((intptr_t)pPhys2Virt - (intptr_t)pHead)
1402 | (pHead->offNextAlias & ~PGMPHYS2VIRTHANDLER_OFF_MASK);
1403 Log(("pgmHandlerVirtualInsertAliased: %RGp-%RGp offNextAlias=%#RX32\n", pPhys2Virt->Core.Key, pPhys2Virt->Core.KeyLast, pPhys2Virt->offNextAlias));
1404}
1405
1406
1407/**
1408 * Resets one virtual handler range.
1409 *
1410 * This is called by HandlerVirtualUpdate when it has detected some kind of
1411 * problem and have started clearing the virtual handler page states (or
1412 * when there have been registration/deregistrations). For this reason this
1413 * function will only update the page status if it's lower than desired.
1414 *
1415 * @returns 0
1416 * @param pNode Pointer to a PGMVIRTHANDLER.
1417 * @param pvUser Pointer to the VM.
1418 */
1419DECLCALLBACK(int) pgmHandlerVirtualResetOne(PAVLROGCPTRNODECORE pNode, void *pvUser)
1420{
1421 PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)pNode;
1422 PVM pVM = (PVM)pvUser;
1423
1424 PGM_LOCK_ASSERT_OWNER(pVM);
1425
1426 /*
1427 * Iterate the pages and apply the new state.
1428 */
1429 unsigned uState = pgmHandlerVirtualCalcState(pCur);
1430 PPGMRAMRANGE pRamHint = NULL;
1431 RTGCUINTPTR offPage = ((RTGCUINTPTR)pCur->Core.Key & PAGE_OFFSET_MASK);
1432 RTGCUINTPTR cbLeft = pCur->cb;
1433 for (unsigned iPage = 0; iPage < pCur->cPages; iPage++)
1434 {
1435 PPGMPHYS2VIRTHANDLER pPhys2Virt = &pCur->aPhysToVirt[iPage];
1436 if (pPhys2Virt->Core.Key != NIL_RTGCPHYS)
1437 {
1438 /*
1439 * Update the page state wrt virtual handlers.
1440 */
1441 PPGMPAGE pPage;
1442 int rc = pgmPhysGetPageWithHintEx(pVM, pPhys2Virt->Core.Key, &pPage, &pRamHint);
1443 if ( RT_SUCCESS(rc)
1444 && PGM_PAGE_GET_HNDL_VIRT_STATE(pPage) < uState)
1445 PGM_PAGE_SET_HNDL_VIRT_STATE(pPage, uState);
1446 else
1447 AssertRC(rc);
1448
1449 /*
1450 * Need to insert the page in the Phys2Virt lookup tree?
1451 */
1452 if (pPhys2Virt->Core.KeyLast == NIL_RTGCPHYS)
1453 {
1454#ifdef VBOX_STRICT_PGM_HANDLER_VIRTUAL
1455 AssertRelease(!pPhys2Virt->offNextAlias);
1456#endif
1457 unsigned cbPhys = cbLeft;
1458 if (cbPhys > PAGE_SIZE - offPage)
1459 cbPhys = PAGE_SIZE - offPage;
1460 else
1461 Assert(iPage == pCur->cPages - 1);
1462 pPhys2Virt->Core.KeyLast = pPhys2Virt->Core.Key + cbPhys - 1; /* inclusive */
1463 pPhys2Virt->offNextAlias = PGMPHYS2VIRTHANDLER_IS_HEAD | PGMPHYS2VIRTHANDLER_IN_TREE;
1464 if (!RTAvlroGCPhysInsert(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers, &pPhys2Virt->Core))
1465 pgmHandlerVirtualInsertAliased(pVM, pPhys2Virt);
1466#ifdef VBOX_STRICT_PGM_HANDLER_VIRTUAL
1467 else
1468 AssertReleaseMsg(RTAvlroGCPhysGet(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers, pPhys2Virt->Core.Key) == &pPhys2Virt->Core,
1469 ("%RGp-%RGp offNextAlias=%#RX32\n",
1470 pPhys2Virt->Core.Key, pPhys2Virt->Core.KeyLast, pPhys2Virt->offNextAlias));
1471#endif
1472 Log2(("PHYS2VIRT: Insert physical range %RGp-%RGp offNextAlias=%#RX32 %s\n",
1473 pPhys2Virt->Core.Key, pPhys2Virt->Core.KeyLast, pPhys2Virt->offNextAlias, R3STRING(pCur->pszDesc)));
1474 }
1475 }
1476 cbLeft -= PAGE_SIZE - offPage;
1477 offPage = 0;
1478 }
1479
1480 return 0;
1481}
1482
1483#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
1484
1485/**
1486 * Worker for pgmHandlerVirtualDumpPhysPages.
1487 *
1488 * @returns 0 (continue enumeration).
1489 * @param pNode The virtual handler node.
1490 * @param pvUser User argument, unused.
1491 */
1492static DECLCALLBACK(int) pgmHandlerVirtualDumpPhysPagesCallback(PAVLROGCPHYSNODECORE pNode, void *pvUser)
1493{
1494 PPGMPHYS2VIRTHANDLER pCur = (PPGMPHYS2VIRTHANDLER)pNode;
1495 PPGMVIRTHANDLER pVirt = (PPGMVIRTHANDLER)((uintptr_t)pCur + pCur->offVirtHandler);
1496 NOREF(pvUser); NOREF(pVirt);
1497
1498 Log(("PHYS2VIRT: Range %RGp-%RGp for virtual handler: %s\n", pCur->Core.Key, pCur->Core.KeyLast, pVirt->pszDesc));
1499 return 0;
1500}
1501
1502
1503/**
1504 * Assertion / logging helper for dumping all the
1505 * virtual handlers to the log.
1506 *
1507 * @param pVM Pointer to the VM.
1508 */
1509void pgmHandlerVirtualDumpPhysPages(PVM pVM)
1510{
1511 RTAvlroGCPhysDoWithAll(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers, true /* from left */,
1512 pgmHandlerVirtualDumpPhysPagesCallback, 0);
1513}
1514
1515#endif /* VBOX_STRICT || LOG_ENABLED */
1516#ifdef VBOX_STRICT
1517
1518/**
1519 * State structure used by the PGMAssertHandlerAndFlagsInSync() function
1520 * and its AVL enumerators.
1521 */
1522typedef struct PGMAHAFIS
1523{
1524 /** The current physical address. */
1525 RTGCPHYS GCPhys;
1526 /** The state we've calculated. */
1527 unsigned uVirtStateFound;
1528 /** The state we're matching up to. */
1529 unsigned uVirtState;
1530 /** Number of errors. */
1531 unsigned cErrors;
1532 /** Pointer to the VM. */
1533 PVM pVM;
1534} PGMAHAFIS, *PPGMAHAFIS;
1535
1536
1537#if 0 /* unused */
1538/**
1539 * Verify virtual handler by matching physical address.
1540 *
1541 * @returns 0
1542 * @param pNode Pointer to a PGMVIRTHANDLER.
1543 * @param pvUser Pointer to user parameter.
1544 */
1545static DECLCALLBACK(int) pgmHandlerVirtualVerifyOneByPhysAddr(PAVLROGCPTRNODECORE pNode, void *pvUser)
1546{
1547 PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)pNode;
1548 PPGMAHAFIS pState = (PPGMAHAFIS)pvUser;
1549
1550 for (unsigned iPage = 0; iPage < pCur->cPages; iPage++)
1551 {
1552 if ((pCur->aPhysToVirt[iPage].Core.Key & X86_PTE_PAE_PG_MASK) == pState->GCPhys)
1553 {
1554 unsigned uState = pgmHandlerVirtualCalcState(pCur);
1555 if (pState->uVirtState < uState)
1556 {
1557 error
1558 }
1559
1560 if (pState->uVirtState == uState)
1561 break; //??
1562 }
1563 }
1564 return 0;
1565}
1566#endif /* unused */
1567
1568
1569/**
1570 * Verify a virtual handler (enumeration callback).
1571 *
1572 * Called by PGMAssertHandlerAndFlagsInSync to check the sanity of all
1573 * the virtual handlers, esp. that the physical addresses matches up.
1574 *
1575 * @returns 0
1576 * @param pNode Pointer to a PGMVIRTHANDLER.
1577 * @param pvUser Pointer to a PPGMAHAFIS structure.
1578 */
1579static DECLCALLBACK(int) pgmHandlerVirtualVerifyOne(PAVLROGCPTRNODECORE pNode, void *pvUser)
1580{
1581 PPGMVIRTHANDLER pVirt = (PPGMVIRTHANDLER)pNode;
1582 PPGMAHAFIS pState = (PPGMAHAFIS)pvUser;
1583 PVM pVM = pState->pVM;
1584
1585 /*
1586 * Validate the type and calc state.
1587 */
1588 switch (pVirt->enmType)
1589 {
1590 case PGMVIRTHANDLERTYPE_WRITE:
1591 case PGMVIRTHANDLERTYPE_ALL:
1592 break;
1593 default:
1594 AssertMsgFailed(("unknown/wrong enmType=%d\n", pVirt->enmType));
1595 pState->cErrors++;
1596 return 0;
1597 }
1598 const unsigned uState = pgmHandlerVirtualCalcState(pVirt);
1599
1600 /*
1601 * Check key alignment.
1602 */
1603 if ( (pVirt->aPhysToVirt[0].Core.Key & PAGE_OFFSET_MASK) != ((RTGCUINTPTR)pVirt->Core.Key & PAGE_OFFSET_MASK)
1604 && pVirt->aPhysToVirt[0].Core.Key != NIL_RTGCPHYS)
1605 {
1606 AssertMsgFailed(("virt handler phys has incorrect key! %RGp %RGv %s\n",
1607 pVirt->aPhysToVirt[0].Core.Key, pVirt->Core.Key, R3STRING(pVirt->pszDesc)));
1608 pState->cErrors++;
1609 }
1610
1611 if ( (pVirt->aPhysToVirt[pVirt->cPages - 1].Core.KeyLast & PAGE_OFFSET_MASK) != ((RTGCUINTPTR)pVirt->Core.KeyLast & PAGE_OFFSET_MASK)
1612 && pVirt->aPhysToVirt[pVirt->cPages - 1].Core.Key != NIL_RTGCPHYS)
1613 {
1614 AssertMsgFailed(("virt handler phys has incorrect key! %RGp %RGv %s\n",
1615 pVirt->aPhysToVirt[pVirt->cPages - 1].Core.KeyLast, pVirt->Core.KeyLast, R3STRING(pVirt->pszDesc)));
1616 pState->cErrors++;
1617 }
1618
1619 /*
1620 * Check pages for sanity and state.
1621 */
1622 RTGCUINTPTR GCPtr = (RTGCUINTPTR)pVirt->Core.Key;
1623 for (unsigned iPage = 0; iPage < pVirt->cPages; iPage++, GCPtr += PAGE_SIZE)
1624 {
1625 for (VMCPUID i = 0; i < pVM->cCpus; i++)
1626 {
1627 PVMCPU pVCpu = &pVM->aCpus[i];
1628
1629 RTGCPHYS GCPhysGst;
1630 uint64_t fGst;
1631 int rc = PGMGstGetPage(pVCpu, (RTGCPTR)GCPtr, &fGst, &GCPhysGst);
1632 if ( rc == VERR_PAGE_NOT_PRESENT
1633 || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1634 {
1635 if (pVirt->aPhysToVirt[iPage].Core.Key != NIL_RTGCPHYS)
1636 {
1637 AssertMsgFailed(("virt handler phys out of sync. %RGp GCPhysNew=~0 iPage=%#x %RGv %s\n",
1638 pVirt->aPhysToVirt[iPage].Core.Key, iPage, GCPtr, R3STRING(pVirt->pszDesc)));
1639 pState->cErrors++;
1640 }
1641 continue;
1642 }
1643
1644 AssertRCReturn(rc, 0);
1645 if ((pVirt->aPhysToVirt[iPage].Core.Key & X86_PTE_PAE_PG_MASK) != GCPhysGst)
1646 {
1647 AssertMsgFailed(("virt handler phys out of sync. %RGp GCPhysGst=%RGp iPage=%#x %RGv %s\n",
1648 pVirt->aPhysToVirt[iPage].Core.Key, GCPhysGst, iPage, GCPtr, R3STRING(pVirt->pszDesc)));
1649 pState->cErrors++;
1650 continue;
1651 }
1652
1653 PPGMPAGE pPage = pgmPhysGetPage(pVM, GCPhysGst);
1654 if (!pPage)
1655 {
1656 AssertMsgFailed(("virt handler getting ram flags. GCPhysGst=%RGp iPage=%#x %RGv %s\n",
1657 GCPhysGst, iPage, GCPtr, R3STRING(pVirt->pszDesc)));
1658 pState->cErrors++;
1659 continue;
1660 }
1661
1662 if (PGM_PAGE_GET_HNDL_VIRT_STATE(pPage) < uState)
1663 {
1664 AssertMsgFailed(("virt handler state mismatch. pPage=%R[pgmpage] GCPhysGst=%RGp iPage=%#x %RGv state=%d expected>=%d %s\n",
1665 pPage, GCPhysGst, iPage, GCPtr, PGM_PAGE_GET_HNDL_VIRT_STATE(pPage), uState, R3STRING(pVirt->pszDesc)));
1666 pState->cErrors++;
1667 continue;
1668 }
1669 } /* for each VCPU */
1670 } /* for pages in virtual mapping. */
1671
1672 return 0;
1673}
1674
1675
1676/**
1677 * Asserts that the handlers+guest-page-tables == ramrange-flags and
1678 * that the physical addresses associated with virtual handlers are correct.
1679 *
1680 * @returns Number of mismatches.
1681 * @param pVM Pointer to the VM.
1682 */
1683VMMDECL(unsigned) PGMAssertHandlerAndFlagsInSync(PVM pVM)
1684{
1685 PPGM pPGM = &pVM->pgm.s;
1686 PGMAHAFIS State;
1687 State.GCPhys = 0;
1688 State.uVirtState = 0;
1689 State.uVirtStateFound = 0;
1690 State.cErrors = 0;
1691 State.pVM = pVM;
1692
1693 PGM_LOCK_ASSERT_OWNER(pVM);
1694
1695 /*
1696 * Check the RAM flags against the handlers.
1697 */
1698 for (PPGMRAMRANGE pRam = pPGM->CTX_SUFF(pRamRangesX); pRam; pRam = pRam->CTX_SUFF(pNext))
1699 {
1700 const uint32_t cPages = pRam->cb >> PAGE_SHIFT;
1701 for (uint32_t iPage = 0; iPage < cPages; iPage++)
1702 {
1703 PGMPAGE const *pPage = &pRam->aPages[iPage];
1704 if (PGM_PAGE_HAS_ANY_HANDLERS(pPage))
1705 {
1706 State.GCPhys = pRam->GCPhys + (iPage << PAGE_SHIFT);
1707
1708 /*
1709 * Physical first - calculate the state based on the handlers
1710 * active on the page, then compare.
1711 */
1712 if (PGM_PAGE_HAS_ANY_PHYSICAL_HANDLERS(pPage))
1713 {
1714 /* the first */
1715 PPGMPHYSHANDLER pPhys = (PPGMPHYSHANDLER)RTAvlroGCPhysRangeGet(&pPGM->CTX_SUFF(pTrees)->PhysHandlers, State.GCPhys);
1716 if (!pPhys)
1717 {
1718 pPhys = (PPGMPHYSHANDLER)RTAvlroGCPhysGetBestFit(&pPGM->CTX_SUFF(pTrees)->PhysHandlers, State.GCPhys, true);
1719 if ( pPhys
1720 && pPhys->Core.Key > (State.GCPhys + PAGE_SIZE - 1))
1721 pPhys = NULL;
1722 Assert(!pPhys || pPhys->Core.Key >= State.GCPhys);
1723 }
1724 if (pPhys)
1725 {
1726 unsigned uState = pgmHandlerPhysicalCalcState(pPhys);
1727
1728 /* more? */
1729 while (pPhys->Core.KeyLast < (State.GCPhys | PAGE_OFFSET_MASK))
1730 {
1731 PPGMPHYSHANDLER pPhys2 = (PPGMPHYSHANDLER)RTAvlroGCPhysGetBestFit(&pPGM->CTX_SUFF(pTrees)->PhysHandlers,
1732 pPhys->Core.KeyLast + 1, true);
1733 if ( !pPhys2
1734 || pPhys2->Core.Key > (State.GCPhys | PAGE_OFFSET_MASK))
1735 break;
1736 unsigned uState2 = pgmHandlerPhysicalCalcState(pPhys2);
1737 uState = RT_MAX(uState, uState2);
1738 pPhys = pPhys2;
1739 }
1740
1741 /* compare.*/
1742 if ( PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) != uState
1743 && PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) != PGM_PAGE_HNDL_PHYS_STATE_DISABLED)
1744 {
1745 AssertMsgFailed(("ram range vs phys handler flags mismatch. GCPhys=%RGp state=%d expected=%d %s\n",
1746 State.GCPhys, PGM_PAGE_GET_HNDL_PHYS_STATE(pPage), uState, pPhys->pszDesc));
1747 State.cErrors++;
1748 }
1749
1750#ifdef VBOX_WITH_REM
1751# ifdef IN_RING3
1752 /* validate that REM is handling it. */
1753 if ( !REMR3IsPageAccessHandled(pVM, State.GCPhys)
1754 /* ignore shadowed ROM for the time being. */
1755 && PGM_PAGE_GET_TYPE(pPage) != PGMPAGETYPE_ROM_SHADOW)
1756 {
1757 AssertMsgFailed(("ram range vs phys handler REM mismatch. GCPhys=%RGp state=%d %s\n",
1758 State.GCPhys, PGM_PAGE_GET_HNDL_PHYS_STATE(pPage), pPhys->pszDesc));
1759 State.cErrors++;
1760 }
1761# endif
1762#endif
1763 }
1764 else
1765 {
1766 AssertMsgFailed(("ram range vs phys handler mismatch. no handler for GCPhys=%RGp\n", State.GCPhys));
1767 State.cErrors++;
1768 }
1769 }
1770
1771 /*
1772 * Virtual handlers.
1773 */
1774 if (PGM_PAGE_HAS_ACTIVE_VIRTUAL_HANDLERS(pPage))
1775 {
1776 State.uVirtState = PGM_PAGE_GET_HNDL_VIRT_STATE(pPage);
1777#if 1
1778 /* locate all the matching physical ranges. */
1779 State.uVirtStateFound = PGM_PAGE_HNDL_VIRT_STATE_NONE;
1780 RTGCPHYS GCPhysKey = State.GCPhys;
1781 for (;;)
1782 {
1783 PPGMPHYS2VIRTHANDLER pPhys2Virt = (PPGMPHYS2VIRTHANDLER)RTAvlroGCPhysGetBestFit(&pVM->pgm.s.CTX_SUFF(pTrees)->PhysToVirtHandlers,
1784 GCPhysKey, true /* above-or-equal */);
1785 if ( !pPhys2Virt
1786 || (pPhys2Virt->Core.Key & X86_PTE_PAE_PG_MASK) != State.GCPhys)
1787 break;
1788
1789 /* the head */
1790 GCPhysKey = pPhys2Virt->Core.KeyLast;
1791 PPGMVIRTHANDLER pCur = (PPGMVIRTHANDLER)((uintptr_t)pPhys2Virt + pPhys2Virt->offVirtHandler);
1792 unsigned uState = pgmHandlerVirtualCalcState(pCur);
1793 State.uVirtStateFound = RT_MAX(State.uVirtStateFound, uState);
1794
1795 /* any aliases */
1796 while (pPhys2Virt->offNextAlias & PGMPHYS2VIRTHANDLER_OFF_MASK)
1797 {
1798 pPhys2Virt = (PPGMPHYS2VIRTHANDLER)((uintptr_t)pPhys2Virt + (pPhys2Virt->offNextAlias & PGMPHYS2VIRTHANDLER_OFF_MASK));
1799 pCur = (PPGMVIRTHANDLER)((uintptr_t)pPhys2Virt + pPhys2Virt->offVirtHandler);
1800 uState = pgmHandlerVirtualCalcState(pCur);
1801 State.uVirtStateFound = RT_MAX(State.uVirtStateFound, uState);
1802 }
1803
1804 /* done? */
1805 if ((GCPhysKey & X86_PTE_PAE_PG_MASK) != State.GCPhys)
1806 break;
1807 }
1808#else
1809 /* very slow */
1810 RTAvlroGCPtrDoWithAll(&pVM->pgm.s.CTX_SUFF(pTrees)->VirtHandlers, true, pgmHandlerVirtualVerifyOneByPhysAddr, &State);
1811#endif
1812 if (State.uVirtState != State.uVirtStateFound)
1813 {
1814 AssertMsgFailed(("ram range vs virt handler flags mismatch. GCPhys=%RGp uVirtState=%#x uVirtStateFound=%#x\n",
1815 State.GCPhys, State.uVirtState, State.uVirtStateFound));
1816 State.cErrors++;
1817 }
1818 }
1819 }
1820 } /* foreach page in ram range. */
1821 } /* foreach ram range. */
1822
1823 /*
1824 * Check that the physical addresses of the virtual handlers matches up
1825 * and that they are otherwise sane.
1826 */
1827 RTAvlroGCPtrDoWithAll(&pVM->pgm.s.CTX_SUFF(pTrees)->VirtHandlers, true, pgmHandlerVirtualVerifyOne, &State);
1828
1829 /*
1830 * Do the reverse check for physical handlers.
1831 */
1832 /** @todo */
1833
1834 return State.cErrors;
1835}
1836
1837#endif /* VBOX_STRICT */
1838
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use