VirtualBox

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

Last change on this file since 94620 was 93725, checked in by vboxsync, 3 years ago

VMM: More arm64 adjustments. bugref:9898

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