VirtualBox

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

Last change on this file since 78927 was 78431, checked in by vboxsync, 6 years ago

VMM: Started refactoring GVM & VM structures for bugref:9217

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