VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/PGMR0.cpp@ 67994

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

PGMR0,GVMMR0: Ditto for large page alloc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 24.5 KB
Line 
1/* $Id: PGMR0.cpp 67994 2017-07-17 12:57:08Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor, Ring-0.
4 */
5
6/*
7 * Copyright (C) 2007-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/rawpci.h>
24#include <VBox/vmm/pgm.h>
25#include <VBox/vmm/gmm.h>
26#include <VBox/vmm/gvm.h>
27#include "PGMInternal.h"
28#include <VBox/vmm/vm.h>
29#include "PGMInline.h"
30#include <VBox/log.h>
31#include <VBox/err.h>
32#include <iprt/assert.h>
33#include <iprt/mem.h>
34
35
36/*
37 * Instantiate the ring-0 header/code templates.
38 */
39#define PGM_BTH_NAME(name) PGM_BTH_NAME_32BIT_PROT(name)
40#include "PGMR0Bth.h"
41#undef PGM_BTH_NAME
42
43#define PGM_BTH_NAME(name) PGM_BTH_NAME_PAE_PROT(name)
44#include "PGMR0Bth.h"
45#undef PGM_BTH_NAME
46
47#define PGM_BTH_NAME(name) PGM_BTH_NAME_AMD64_PROT(name)
48#include "PGMR0Bth.h"
49#undef PGM_BTH_NAME
50
51#define PGM_BTH_NAME(name) PGM_BTH_NAME_EPT_PROT(name)
52#include "PGMR0Bth.h"
53#undef PGM_BTH_NAME
54
55
56/**
57 * Worker function for PGMR3PhysAllocateHandyPages and pgmPhysEnsureHandyPage.
58 *
59 * @returns The following VBox status codes.
60 * @retval VINF_SUCCESS on success. FF cleared.
61 * @retval VINF_EM_NO_MEMORY if we're out of memory. The FF is set in this case.
62 *
63 * @param pGVM The global (ring-0) VM structure.
64 * @param pVM The cross context VM structure.
65 * @param idCpu The ID of the calling EMT.
66 *
67 * @thread EMT(idCpu)
68 *
69 * @remarks Must be called from within the PGM critical section. The caller
70 * must clear the new pages.
71 */
72VMMR0_INT_DECL(int) PGMR0PhysAllocateHandyPages(PGVM pGVM, PVM pVM, VMCPUID idCpu)
73{
74 /*
75 * Validate inputs.
76 */
77 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID); /* caller already checked this, but just to be sure. */
78 AssertReturn(pGVM->aCpus[idCpu].hEMT == RTThreadNativeSelf(), VERR_NOT_OWNER);
79 PVMCPU pVCpu = &pVM->aCpus[idCpu];
80
81 PGM_LOCK_ASSERT_OWNER_EX(pVM, pVCpu);
82
83 /*
84 * Check for error injection.
85 */
86 if (RT_UNLIKELY(pVM->pgm.s.fErrInjHandyPages))
87 return VERR_NO_MEMORY;
88
89 /*
90 * Try allocate a full set of handy pages.
91 */
92 uint32_t iFirst = pVM->pgm.s.cHandyPages;
93 AssertReturn(iFirst <= RT_ELEMENTS(pVM->pgm.s.aHandyPages), VERR_PGM_HANDY_PAGE_IPE);
94 uint32_t cPages = RT_ELEMENTS(pVM->pgm.s.aHandyPages) - iFirst;
95 if (!cPages)
96 return VINF_SUCCESS;
97 int rc = GMMR0AllocateHandyPages(pGVM, pVM, idCpu, cPages, cPages, &pVM->pgm.s.aHandyPages[iFirst]);
98 if (RT_SUCCESS(rc))
99 {
100#ifdef VBOX_STRICT
101 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
102 {
103 Assert(pVM->pgm.s.aHandyPages[i].idPage != NIL_GMM_PAGEID);
104 Assert(pVM->pgm.s.aHandyPages[i].idPage <= GMM_PAGEID_LAST);
105 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
106 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys != NIL_RTHCPHYS);
107 Assert(!(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys & ~X86_PTE_PAE_PG_MASK));
108 }
109#endif
110
111 pVM->pgm.s.cHandyPages = RT_ELEMENTS(pVM->pgm.s.aHandyPages);
112 }
113 else if (rc != VERR_GMM_SEED_ME)
114 {
115 if ( ( rc == VERR_GMM_HIT_GLOBAL_LIMIT
116 || rc == VERR_GMM_HIT_VM_ACCOUNT_LIMIT)
117 && iFirst < PGM_HANDY_PAGES_MIN)
118 {
119
120#ifdef VBOX_STRICT
121 /* We're ASSUMING that GMM has updated all the entires before failing us. */
122 uint32_t i;
123 for (i = iFirst; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
124 {
125 Assert(pVM->pgm.s.aHandyPages[i].idPage == NIL_GMM_PAGEID);
126 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
127 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys == NIL_RTHCPHYS);
128 }
129#endif
130
131 /*
132 * Reduce the number of pages until we hit the minimum limit.
133 */
134 do
135 {
136 cPages >>= 1;
137 if (cPages + iFirst < PGM_HANDY_PAGES_MIN)
138 cPages = PGM_HANDY_PAGES_MIN - iFirst;
139 rc = GMMR0AllocateHandyPages(pGVM, pVM, idCpu, 0, cPages, &pVM->pgm.s.aHandyPages[iFirst]);
140 } while ( ( rc == VERR_GMM_HIT_GLOBAL_LIMIT
141 || rc == VERR_GMM_HIT_VM_ACCOUNT_LIMIT)
142 && cPages + iFirst > PGM_HANDY_PAGES_MIN);
143 if (RT_SUCCESS(rc))
144 {
145#ifdef VBOX_STRICT
146 i = iFirst + cPages;
147 while (i-- > 0)
148 {
149 Assert(pVM->pgm.s.aHandyPages[i].idPage != NIL_GMM_PAGEID);
150 Assert(pVM->pgm.s.aHandyPages[i].idPage <= GMM_PAGEID_LAST);
151 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
152 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys != NIL_RTHCPHYS);
153 Assert(!(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys & ~X86_PTE_PAE_PG_MASK));
154 }
155
156 for (i = cPages + iFirst; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
157 {
158 Assert(pVM->pgm.s.aHandyPages[i].idPage == NIL_GMM_PAGEID);
159 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
160 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys == NIL_RTHCPHYS);
161 }
162#endif
163
164 pVM->pgm.s.cHandyPages = iFirst + cPages;
165 }
166 }
167
168 if (RT_FAILURE(rc) && rc != VERR_GMM_SEED_ME)
169 {
170 LogRel(("PGMR0PhysAllocateHandyPages: rc=%Rrc iFirst=%d cPages=%d\n", rc, iFirst, cPages));
171 VM_FF_SET(pVM, VM_FF_PGM_NO_MEMORY);
172 }
173 }
174
175
176 LogFlow(("PGMR0PhysAllocateHandyPages: cPages=%d rc=%Rrc\n", cPages, rc));
177 return rc;
178}
179
180
181/**
182 * Flushes any changes pending in the handy page array.
183 *
184 * It is very important that this gets done when page sharing is enabled.
185 *
186 * @returns The following VBox status codes.
187 * @retval VINF_SUCCESS on success. FF cleared.
188 *
189 * @param pGVM The global (ring-0) VM structure.
190 * @param pVM The cross context VM structure.
191 * @param idCpu The ID of the calling EMT.
192 *
193 * @thread EMT(idCpu)
194 *
195 * @remarks Must be called from within the PGM critical section.
196 */
197VMMR0_INT_DECL(int) PGMR0PhysFlushHandyPages(PGVM pGVM, PVM pVM, VMCPUID idCpu)
198{
199 /*
200 * Validate inputs.
201 */
202 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID); /* caller already checked this, but just to be sure. */
203 AssertReturn(pGVM->aCpus[idCpu].hEMT == RTThreadNativeSelf(), VERR_NOT_OWNER);
204 PVMCPU pVCpu = &pVM->aCpus[idCpu];
205
206 PGM_LOCK_ASSERT_OWNER_EX(pVM, pVCpu);
207
208 /*
209 * Try allocate a full set of handy pages.
210 */
211 uint32_t iFirst = pVM->pgm.s.cHandyPages;
212 AssertReturn(iFirst <= RT_ELEMENTS(pVM->pgm.s.aHandyPages), VERR_PGM_HANDY_PAGE_IPE);
213 uint32_t cPages = RT_ELEMENTS(pVM->pgm.s.aHandyPages) - iFirst;
214 if (!cPages)
215 return VINF_SUCCESS;
216 int rc = GMMR0AllocateHandyPages(pGVM, pVM, idCpu, cPages, 0, &pVM->pgm.s.aHandyPages[iFirst]);
217
218 LogFlow(("PGMR0PhysFlushHandyPages: cPages=%d rc=%Rrc\n", cPages, rc));
219 return rc;
220}
221
222
223/**
224 * Worker function for PGMR3PhysAllocateLargeHandyPage
225 *
226 * @returns The following VBox status codes.
227 * @retval VINF_SUCCESS on success.
228 * @retval VINF_EM_NO_MEMORY if we're out of memory.
229 *
230 * @param pGVM The global (ring-0) VM structure.
231 * @param pVM The cross context VM structure.
232 * @param idCpu The ID of the calling EMT.
233 *
234 * @thread EMT(idCpu)
235 *
236 * @remarks Must be called from within the PGM critical section. The caller
237 * must clear the new pages.
238 */
239VMMR0_INT_DECL(int) PGMR0PhysAllocateLargeHandyPage(PGVM pGVM, PVM pVM, VMCPUID idCpu)
240{
241 /*
242 * Validate inputs.
243 */
244 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID); /* caller already checked this, but just to be sure. */
245 AssertReturn(pGVM->aCpus[idCpu].hEMT == RTThreadNativeSelf(), VERR_NOT_OWNER);
246 PVMCPU pVCpu = &pVM->aCpus[idCpu];
247
248 PGM_LOCK_ASSERT_OWNER_EX(pVM, pVCpu);
249
250 Assert(!pVM->pgm.s.cLargeHandyPages);
251
252 /*
253 * Do the job.
254 */
255 int rc = GMMR0AllocateLargePage(pGVM, pVM, idCpu, _2M,
256 &pVM->pgm.s.aLargeHandyPage[0].idPage,
257 &pVM->pgm.s.aLargeHandyPage[0].HCPhysGCPhys);
258 if (RT_SUCCESS(rc))
259 pVM->pgm.s.cLargeHandyPages = 1;
260
261 return rc;
262}
263
264
265#ifdef VBOX_WITH_PCI_PASSTHROUGH
266/* Interface sketch. The interface belongs to a global PCI pass-through
267 manager. It shall use the global VM handle, not the user VM handle to
268 store the per-VM info (domain) since that is all ring-0 stuff, thus
269 passing pGVM here. I've tentitively prefixed the functions 'GPciRawR0',
270 we can discuss the PciRaw code re-organtization when I'm back from
271 vacation.
272
273 I've implemented the initial IOMMU set up below. For things to work
274 reliably, we will probably need add a whole bunch of checks and
275 GPciRawR0GuestPageUpdate call to the PGM code. For the present,
276 assuming nested paging (enforced) and prealloc (enforced), no
277 ballooning (check missing), page sharing (check missing) or live
278 migration (check missing), it might work fine. At least if some
279 VM power-off hook is present and can tear down the IOMMU page tables. */
280
281/**
282 * Tells the global PCI pass-through manager that we are about to set up the
283 * guest page to host page mappings for the specfied VM.
284 *
285 * @returns VBox status code.
286 *
287 * @param pGVM The ring-0 VM structure.
288 */
289VMMR0_INT_DECL(int) GPciRawR0GuestPageBeginAssignments(PGVM pGVM)
290{
291 NOREF(pGVM);
292 return VINF_SUCCESS;
293}
294
295
296/**
297 * Assigns a host page mapping for a guest page.
298 *
299 * This is only used when setting up the mappings, i.e. between
300 * GPciRawR0GuestPageBeginAssignments and GPciRawR0GuestPageEndAssignments.
301 *
302 * @returns VBox status code.
303 * @param pGVM The ring-0 VM structure.
304 * @param GCPhys The address of the guest page (page aligned).
305 * @param HCPhys The address of the host page (page aligned).
306 */
307VMMR0_INT_DECL(int) GPciRawR0GuestPageAssign(PGVM pGVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys)
308{
309 AssertReturn(!(GCPhys & PAGE_OFFSET_MASK), VERR_INTERNAL_ERROR_3);
310 AssertReturn(!(HCPhys & PAGE_OFFSET_MASK), VERR_INTERNAL_ERROR_3);
311
312 if (pGVM->rawpci.s.pfnContigMemInfo)
313 /** @todo what do we do on failure? */
314 pGVM->rawpci.s.pfnContigMemInfo(&pGVM->rawpci.s, HCPhys, GCPhys, PAGE_SIZE, PCIRAW_MEMINFO_MAP);
315
316 return VINF_SUCCESS;
317}
318
319
320/**
321 * Indicates that the specified guest page doesn't exists but doesn't have host
322 * page mapping we trust PCI pass-through with.
323 *
324 * This is only used when setting up the mappings, i.e. between
325 * GPciRawR0GuestPageBeginAssignments and GPciRawR0GuestPageEndAssignments.
326 *
327 * @returns VBox status code.
328 * @param pGVM The ring-0 VM structure.
329 * @param GCPhys The address of the guest page (page aligned).
330 * @param HCPhys The address of the host page (page aligned).
331 */
332VMMR0_INT_DECL(int) GPciRawR0GuestPageUnassign(PGVM pGVM, RTGCPHYS GCPhys)
333{
334 AssertReturn(!(GCPhys & PAGE_OFFSET_MASK), VERR_INTERNAL_ERROR_3);
335
336 if (pGVM->rawpci.s.pfnContigMemInfo)
337 /** @todo what do we do on failure? */
338 pGVM->rawpci.s.pfnContigMemInfo(&pGVM->rawpci.s, 0, GCPhys, PAGE_SIZE, PCIRAW_MEMINFO_UNMAP);
339
340 return VINF_SUCCESS;
341}
342
343
344/**
345 * Tells the global PCI pass-through manager that we have completed setting up
346 * the guest page to host page mappings for the specfied VM.
347 *
348 * This complements GPciRawR0GuestPageBeginAssignments and will be called even
349 * if some page assignment failed.
350 *
351 * @returns VBox status code.
352 *
353 * @param pGVM The ring-0 VM structure.
354 */
355VMMR0_INT_DECL(int) GPciRawR0GuestPageEndAssignments(PGVM pGVM)
356{
357 NOREF(pGVM);
358 return VINF_SUCCESS;
359}
360
361
362/**
363 * Tells the global PCI pass-through manager that a guest page mapping has
364 * changed after the initial setup.
365 *
366 * @returns VBox status code.
367 * @param pGVM The ring-0 VM structure.
368 * @param GCPhys The address of the guest page (page aligned).
369 * @param HCPhys The new host page address or NIL_RTHCPHYS if
370 * now unassigned.
371 */
372VMMR0_INT_DECL(int) GPciRawR0GuestPageUpdate(PGVM pGVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys)
373{
374 AssertReturn(!(GCPhys & PAGE_OFFSET_MASK), VERR_INTERNAL_ERROR_4);
375 AssertReturn(!(HCPhys & PAGE_OFFSET_MASK) || HCPhys == NIL_RTHCPHYS, VERR_INTERNAL_ERROR_4);
376 NOREF(pGVM);
377 return VINF_SUCCESS;
378}
379
380#endif /* VBOX_WITH_PCI_PASSTHROUGH */
381
382
383/**
384 * Sets up the IOMMU when raw PCI device is enabled.
385 *
386 * @note This is a hack that will probably be remodelled and refined later!
387 *
388 * @returns VBox status code.
389 *
390 * @param pVM The cross context VM structure.
391 */
392VMMR0_INT_DECL(int) PGMR0PhysSetupIommu(PVM pVM)
393{
394 PGVM pGVM;
395 int rc = GVMMR0ByVM(pVM, &pGVM);
396 if (RT_FAILURE(rc))
397 return rc;
398
399#ifdef VBOX_WITH_PCI_PASSTHROUGH
400 if (pVM->pgm.s.fPciPassthrough)
401 {
402 /*
403 * The Simplistic Approach - Enumerate all the pages and call tell the
404 * IOMMU about each of them.
405 */
406 pgmLock(pVM);
407 rc = GPciRawR0GuestPageBeginAssignments(pGVM);
408 if (RT_SUCCESS(rc))
409 {
410 for (PPGMRAMRANGE pRam = pVM->pgm.s.pRamRangesXR0; RT_SUCCESS(rc) && pRam; pRam = pRam->pNextR0)
411 {
412 PPGMPAGE pPage = &pRam->aPages[0];
413 RTGCPHYS GCPhys = pRam->GCPhys;
414 uint32_t cLeft = pRam->cb >> PAGE_SHIFT;
415 while (cLeft-- > 0)
416 {
417 /* Only expose pages that are 100% safe for now. */
418 if ( PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM
419 && PGM_PAGE_GET_STATE(pPage) == PGM_PAGE_STATE_ALLOCATED
420 && !PGM_PAGE_HAS_ANY_HANDLERS(pPage))
421 rc = GPciRawR0GuestPageAssign(pGVM, GCPhys, PGM_PAGE_GET_HCPHYS(pPage));
422 else
423 rc = GPciRawR0GuestPageUnassign(pGVM, GCPhys);
424
425 /* next */
426 pPage++;
427 GCPhys += PAGE_SIZE;
428 }
429 }
430
431 int rc2 = GPciRawR0GuestPageEndAssignments(pGVM);
432 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
433 rc = rc2;
434 }
435 pgmUnlock(pVM);
436 }
437 else
438#endif
439 rc = VERR_NOT_SUPPORTED;
440 return rc;
441}
442
443
444/**
445 * \#PF Handler for nested paging.
446 *
447 * @returns VBox status code (appropriate for trap handling and GC return).
448 * @param pVM The cross context VM structure.
449 * @param pVCpu The cross context virtual CPU structure.
450 * @param enmShwPagingMode Paging mode for the nested page tables.
451 * @param uErr The trap error code.
452 * @param pRegFrame Trap register frame.
453 * @param GCPhysFault The fault address.
454 */
455VMMR0DECL(int) PGMR0Trap0eHandlerNestedPaging(PVM pVM, PVMCPU pVCpu, PGMMODE enmShwPagingMode, RTGCUINT uErr,
456 PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault)
457{
458 int rc;
459
460 LogFlow(("PGMTrap0eHandler: uErr=%RGx GCPhysFault=%RGp eip=%RGv\n", uErr, GCPhysFault, (RTGCPTR)pRegFrame->rip));
461 STAM_PROFILE_START(&pVCpu->pgm.s.StatRZTrap0e, a);
462 STAM_STATS({ pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution) = NULL; } );
463
464 /* AMD uses the host's paging mode; Intel has a single mode (EPT). */
465 AssertMsg( enmShwPagingMode == PGMMODE_32_BIT || enmShwPagingMode == PGMMODE_PAE || enmShwPagingMode == PGMMODE_PAE_NX
466 || enmShwPagingMode == PGMMODE_AMD64 || enmShwPagingMode == PGMMODE_AMD64_NX || enmShwPagingMode == PGMMODE_EPT,
467 ("enmShwPagingMode=%d\n", enmShwPagingMode));
468
469 /* Reserved shouldn't end up here. */
470 Assert(!(uErr & X86_TRAP_PF_RSVD));
471
472#ifdef VBOX_WITH_STATISTICS
473 /*
474 * Error code stats.
475 */
476 if (uErr & X86_TRAP_PF_US)
477 {
478 if (!(uErr & X86_TRAP_PF_P))
479 {
480 if (uErr & X86_TRAP_PF_RW)
481 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSNotPresentWrite);
482 else
483 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSNotPresentRead);
484 }
485 else if (uErr & X86_TRAP_PF_RW)
486 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSWrite);
487 else if (uErr & X86_TRAP_PF_RSVD)
488 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSReserved);
489 else if (uErr & X86_TRAP_PF_ID)
490 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSNXE);
491 else
492 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eUSRead);
493 }
494 else
495 { /* Supervisor */
496 if (!(uErr & X86_TRAP_PF_P))
497 {
498 if (uErr & X86_TRAP_PF_RW)
499 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eSVNotPresentWrite);
500 else
501 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eSVNotPresentRead);
502 }
503 else if (uErr & X86_TRAP_PF_RW)
504 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eSVWrite);
505 else if (uErr & X86_TRAP_PF_ID)
506 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eSNXE);
507 else if (uErr & X86_TRAP_PF_RSVD)
508 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eSVReserved);
509 }
510#endif
511
512 /*
513 * Call the worker.
514 *
515 * Note! We pretend the guest is in protected mode without paging, so we
516 * can use existing code to build the nested page tables.
517 */
518 bool fLockTaken = false;
519 switch (enmShwPagingMode)
520 {
521 case PGMMODE_32_BIT:
522 rc = PGM_BTH_NAME_32BIT_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, GCPhysFault, &fLockTaken);
523 break;
524 case PGMMODE_PAE:
525 case PGMMODE_PAE_NX:
526 rc = PGM_BTH_NAME_PAE_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, GCPhysFault, &fLockTaken);
527 break;
528 case PGMMODE_AMD64:
529 case PGMMODE_AMD64_NX:
530 rc = PGM_BTH_NAME_AMD64_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, GCPhysFault, &fLockTaken);
531 break;
532 case PGMMODE_EPT:
533 rc = PGM_BTH_NAME_EPT_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, GCPhysFault, &fLockTaken);
534 break;
535 default:
536 AssertFailed();
537 rc = VERR_INVALID_PARAMETER;
538 break;
539 }
540 if (fLockTaken)
541 {
542 PGM_LOCK_ASSERT_OWNER(pVM);
543 pgmUnlock(pVM);
544 }
545
546 if (rc == VINF_PGM_SYNCPAGE_MODIFIED_PDE)
547 rc = VINF_SUCCESS;
548 /*
549 * Handle the case where we cannot interpret the instruction because we cannot get the guest physical address
550 * via its page tables, see @bugref{6043}.
551 */
552 else if ( rc == VERR_PAGE_NOT_PRESENT /* SMP only ; disassembly might fail. */
553 || rc == VERR_PAGE_TABLE_NOT_PRESENT /* seen with UNI & SMP */
554 || rc == VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT /* seen with SMP */
555 || rc == VERR_PAGE_MAP_LEVEL4_NOT_PRESENT) /* precaution */
556 {
557 Log(("WARNING: Unexpected VERR_PAGE_TABLE_NOT_PRESENT (%d) for page fault at %RGp error code %x (rip=%RGv)\n", rc, GCPhysFault, uErr, pRegFrame->rip));
558 /* Some kind of inconsistency in the SMP case; it's safe to just execute the instruction again; not sure about
559 single VCPU VMs though. */
560 rc = VINF_SUCCESS;
561 }
562
563 STAM_STATS({ if (!pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution))
564 pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution) = &pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0eTime2Misc; });
565 STAM_PROFILE_STOP_EX(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatRZTrap0e, pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution), a);
566 return rc;
567}
568
569
570/**
571 * \#PF Handler for deliberate nested paging misconfiguration (/reserved bit)
572 * employed for MMIO pages.
573 *
574 * @returns VBox status code (appropriate for trap handling and GC return).
575 * @param pVM The cross context VM structure.
576 * @param pVCpu The cross context virtual CPU structure.
577 * @param enmShwPagingMode Paging mode for the nested page tables.
578 * @param pRegFrame Trap register frame.
579 * @param GCPhysFault The fault address.
580 * @param uErr The error code, UINT32_MAX if not available
581 * (VT-x).
582 */
583VMMR0DECL(VBOXSTRICTRC) PGMR0Trap0eHandlerNPMisconfig(PVM pVM, PVMCPU pVCpu, PGMMODE enmShwPagingMode,
584 PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, uint32_t uErr)
585{
586#ifdef PGM_WITH_MMIO_OPTIMIZATIONS
587 STAM_PROFILE_START(&pVCpu->CTX_SUFF(pStats)->StatR0NpMiscfg, a);
588 VBOXSTRICTRC rc;
589
590 /*
591 * Try lookup the all access physical handler for the address.
592 */
593 pgmLock(pVM);
594 PPGMPHYSHANDLER pHandler = pgmHandlerPhysicalLookup(pVM, GCPhysFault);
595 PPGMPHYSHANDLERTYPEINT pHandlerType = RT_LIKELY(pHandler) ? PGMPHYSHANDLER_GET_TYPE(pVM, pHandler) : NULL;
596 if (RT_LIKELY(pHandler && pHandlerType->enmKind != PGMPHYSHANDLERKIND_WRITE))
597 {
598 /*
599 * If the handle has aliases page or pages that have been temporarily
600 * disabled, we'll have to take a detour to make sure we resync them
601 * to avoid lots of unnecessary exits.
602 */
603 PPGMPAGE pPage;
604 if ( ( pHandler->cAliasedPages
605 || pHandler->cTmpOffPages)
606 && ( (pPage = pgmPhysGetPage(pVM, GCPhysFault)) == NULL
607 || PGM_PAGE_GET_HNDL_PHYS_STATE(pPage) == PGM_PAGE_HNDL_PHYS_STATE_DISABLED)
608 )
609 {
610 Log(("PGMR0Trap0eHandlerNPMisconfig: Resyncing aliases / tmp-off page at %RGp (uErr=%#x) %R[pgmpage]\n", GCPhysFault, uErr, pPage));
611 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatR0NpMiscfgSyncPage);
612 rc = pgmShwSyncNestedPageLocked(pVCpu, GCPhysFault, 1 /*cPages*/, enmShwPagingMode);
613 pgmUnlock(pVM);
614 }
615 else
616 {
617 if (pHandlerType->CTX_SUFF(pfnPfHandler))
618 {
619 void *pvUser = pHandler->CTX_SUFF(pvUser);
620 STAM_PROFILE_START(&pHandler->Stat, h);
621 pgmUnlock(pVM);
622
623 Log6(("PGMR0Trap0eHandlerNPMisconfig: calling %p(,%#x,,%RGp,%p)\n", pHandlerType->CTX_SUFF(pfnPfHandler), uErr, GCPhysFault, pvUser));
624 rc = pHandlerType->CTX_SUFF(pfnPfHandler)(pVM, pVCpu, uErr == UINT32_MAX ? RTGCPTR_MAX : uErr, pRegFrame,
625 GCPhysFault, GCPhysFault, pvUser);
626
627#ifdef VBOX_WITH_STATISTICS
628 pgmLock(pVM);
629 pHandler = pgmHandlerPhysicalLookup(pVM, GCPhysFault);
630 if (pHandler)
631 STAM_PROFILE_STOP(&pHandler->Stat, h);
632 pgmUnlock(pVM);
633#endif
634 }
635 else
636 {
637 pgmUnlock(pVM);
638 Log(("PGMR0Trap0eHandlerNPMisconfig: %RGp (uErr=%#x) -> R3\n", GCPhysFault, uErr));
639 rc = VINF_EM_RAW_EMULATE_INSTR;
640 }
641 }
642 }
643 else
644 {
645 /*
646 * Must be out of sync, so do a SyncPage and restart the instruction.
647 *
648 * ASSUMES that ALL handlers are page aligned and covers whole pages
649 * (assumption asserted in PGMHandlerPhysicalRegisterEx).
650 */
651 Log(("PGMR0Trap0eHandlerNPMisconfig: Out of sync page at %RGp (uErr=%#x)\n", GCPhysFault, uErr));
652 STAM_COUNTER_INC(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatR0NpMiscfgSyncPage);
653 rc = pgmShwSyncNestedPageLocked(pVCpu, GCPhysFault, 1 /*cPages*/, enmShwPagingMode);
654 pgmUnlock(pVM);
655 }
656
657 STAM_PROFILE_STOP(&pVCpu->pgm.s.CTX_SUFF(pStats)->StatR0NpMiscfg, a);
658 return rc;
659
660#else
661 AssertLogRelFailed();
662 return VERR_PGM_NOT_USED_IN_MODE;
663#endif
664}
665
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette