VirtualBox

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

Last change on this file since 80540 was 80346, checked in by vboxsync, 5 years ago

VMM,PciRaw: Eliminate duplicate PGVM/PVMCC and PGVMCPU/PVMCPUCC parameters in ring-0 code. butref:9217

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