VirtualBox

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

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

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