VirtualBox

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

Last change on this file since 30690 was 30111, checked in by vboxsync, 14 years ago

iprt/asm.h,*: Revised the ASMAtomic*Ptr functions and macros. The new saves lots of unsafe (void * volatile *) casts as well as adding some type safety when using GCC (typeof rulez).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.3 KB
Line 
1/* $Id: PGMR0.cpp 30111 2010-06-09 12:14:59Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor, Ring-0.
4 */
5
6/*
7 * Copyright (C) 2007 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* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_PGM
22#include <VBox/pgm.h>
23#include <VBox/gmm.h>
24#include "../PGMInternal.h"
25#include <VBox/vm.h>
26#include "../PGMInline.h"
27#include <VBox/log.h>
28#include <VBox/err.h>
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31
32RT_C_DECLS_BEGIN
33#define PGM_BTH_NAME(name) PGM_BTH_NAME_32BIT_PROT(name)
34#include "PGMR0Bth.h"
35#undef PGM_BTH_NAME
36
37#define PGM_BTH_NAME(name) PGM_BTH_NAME_PAE_PROT(name)
38#include "PGMR0Bth.h"
39#undef PGM_BTH_NAME
40
41#define PGM_BTH_NAME(name) PGM_BTH_NAME_AMD64_PROT(name)
42#include "PGMR0Bth.h"
43#undef PGM_BTH_NAME
44
45#define PGM_BTH_NAME(name) PGM_BTH_NAME_EPT_PROT(name)
46#include "PGMR0Bth.h"
47#undef PGM_BTH_NAME
48
49RT_C_DECLS_END
50
51
52/**
53 * Worker function for PGMR3PhysAllocateHandyPages and pgmPhysEnsureHandyPage.
54 *
55 * @returns The following VBox status codes.
56 * @retval VINF_SUCCESS on success. FF cleared.
57 * @retval VINF_EM_NO_MEMORY if we're out of memory. The FF is set in this case.
58 *
59 * @param pVM The VM handle.
60 * @param pVCpu The VMCPU handle.
61 *
62 * @remarks Must be called from within the PGM critical section. The caller
63 * must clear the new pages.
64 */
65VMMR0DECL(int) PGMR0PhysAllocateHandyPages(PVM pVM, PVMCPU pVCpu)
66{
67 Assert(PDMCritSectIsOwnerEx(&pVM->pgm.s.CritSect, pVCpu->idCpu));
68
69 /*
70 * Check for error injection.
71 */
72 if (RT_UNLIKELY(pVM->pgm.s.fErrInjHandyPages))
73 return VERR_NO_MEMORY;
74
75 /*
76 * Try allocate a full set of handy pages.
77 */
78 uint32_t iFirst = pVM->pgm.s.cHandyPages;
79 AssertReturn(iFirst <= RT_ELEMENTS(pVM->pgm.s.aHandyPages), VERR_INTERNAL_ERROR);
80 uint32_t cPages = RT_ELEMENTS(pVM->pgm.s.aHandyPages) - iFirst;
81 if (!cPages)
82 return VINF_SUCCESS;
83 int rc = GMMR0AllocateHandyPages(pVM, pVCpu->idCpu, cPages, cPages, &pVM->pgm.s.aHandyPages[iFirst]);
84 if (RT_SUCCESS(rc))
85 {
86 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
87 {
88 Assert(pVM->pgm.s.aHandyPages[i].idPage != NIL_GMM_PAGEID);
89 Assert(pVM->pgm.s.aHandyPages[i].idPage <= GMM_PAGEID_LAST);
90 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
91 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys != NIL_RTHCPHYS);
92 Assert(!(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys & ~X86_PTE_PAE_PG_MASK));
93 }
94
95 pVM->pgm.s.cHandyPages = RT_ELEMENTS(pVM->pgm.s.aHandyPages);
96 }
97 else if (rc != VERR_GMM_SEED_ME)
98 {
99 if ( ( rc == VERR_GMM_HIT_GLOBAL_LIMIT
100 || rc == VERR_GMM_HIT_VM_ACCOUNT_LIMIT)
101 && iFirst < PGM_HANDY_PAGES_MIN)
102 {
103
104#ifdef VBOX_STRICT
105 /* We're ASSUMING that GMM has updated all the entires before failing us. */
106 uint32_t i;
107 for (i = iFirst; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
108 {
109 Assert(pVM->pgm.s.aHandyPages[i].idPage == NIL_GMM_PAGEID);
110 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
111 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys == NIL_RTHCPHYS);
112 }
113#endif
114
115 /*
116 * Reduce the number of pages until we hit the minimum limit.
117 */
118 do
119 {
120 cPages >>= 2;
121 if (cPages + iFirst < PGM_HANDY_PAGES_MIN)
122 cPages = PGM_HANDY_PAGES_MIN - iFirst;
123 rc = GMMR0AllocateHandyPages(pVM, pVCpu->idCpu, cPages, cPages, &pVM->pgm.s.aHandyPages[iFirst]);
124 } while ( ( rc == VERR_GMM_HIT_GLOBAL_LIMIT
125 || rc == VERR_GMM_HIT_VM_ACCOUNT_LIMIT)
126 && cPages + iFirst > PGM_HANDY_PAGES_MIN);
127 if (RT_SUCCESS(rc))
128 {
129#ifdef VBOX_STRICT
130 i = iFirst + cPages;
131 while (i-- > 0)
132 {
133 Assert(pVM->pgm.s.aHandyPages[i].idPage != NIL_GMM_PAGEID);
134 Assert(pVM->pgm.s.aHandyPages[i].idPage <= GMM_PAGEID_LAST);
135 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
136 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys != NIL_RTHCPHYS);
137 Assert(!(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys & ~X86_PTE_PAE_PG_MASK));
138 }
139
140 for (i = cPages + iFirst; i < RT_ELEMENTS(pVM->pgm.s.aHandyPages); i++)
141 {
142 Assert(pVM->pgm.s.aHandyPages[i].idPage == NIL_GMM_PAGEID);
143 Assert(pVM->pgm.s.aHandyPages[i].idSharedPage == NIL_GMM_PAGEID);
144 Assert(pVM->pgm.s.aHandyPages[i].HCPhysGCPhys == NIL_RTHCPHYS);
145 }
146#endif
147
148 pVM->pgm.s.cHandyPages = iFirst + cPages;
149 }
150 }
151
152 if (RT_FAILURE(rc) && rc != VERR_GMM_SEED_ME)
153 {
154 LogRel(("PGMR0PhysAllocateHandyPages: rc=%Rrc iFirst=%d cPages=%d\n", rc, iFirst, cPages));
155 VM_FF_SET(pVM, VM_FF_PGM_NO_MEMORY);
156 }
157 }
158
159
160 LogFlow(("PGMR0PhysAllocateHandyPages: cPages=%d rc=%Rrc\n", cPages, rc));
161 return rc;
162}
163
164/**
165 * Worker function for PGMR3PhysAllocateLargeHandyPage
166 *
167 * @returns The following VBox status codes.
168 * @retval VINF_SUCCESS on success.
169 * @retval VINF_EM_NO_MEMORY if we're out of memory.
170 *
171 * @param pVM The VM handle.
172 * @param pVCpu The VMCPU handle.
173 *
174 * @remarks Must be called from within the PGM critical section. The caller
175 * must clear the new pages.
176 */
177VMMR0DECL(int) PGMR0PhysAllocateLargeHandyPage(PVM pVM, PVMCPU pVCpu)
178{
179 Assert(PDMCritSectIsOwnerEx(&pVM->pgm.s.CritSect, pVCpu->idCpu));
180
181 Assert(!pVM->pgm.s.cLargeHandyPages);
182 int rc = GMMR0AllocateLargePage(pVM, pVCpu->idCpu, _2M, &pVM->pgm.s.aLargeHandyPage[0].idPage, &pVM->pgm.s.aLargeHandyPage[0].HCPhysGCPhys);
183 if (RT_SUCCESS(rc))
184 pVM->pgm.s.cLargeHandyPages = 1;
185
186 return rc;
187}
188
189/**
190 * #PF Handler for nested paging.
191 *
192 * @returns VBox status code (appropriate for trap handling and GC return).
193 * @param pVM VM Handle.
194 * @param pVCpu VMCPU Handle.
195 * @param enmShwPagingMode Paging mode for the nested page tables
196 * @param uErr The trap error code.
197 * @param pRegFrame Trap register frame.
198 * @param pvFault The fault address.
199 */
200VMMR0DECL(int) PGMR0Trap0eHandlerNestedPaging(PVM pVM, PVMCPU pVCpu, PGMMODE enmShwPagingMode, RTGCUINT uErr,
201 PCPUMCTXCORE pRegFrame, RTGCPHYS pvFault)
202{
203 int rc;
204
205 LogFlow(("PGMTrap0eHandler: uErr=%RGx pvFault=%RGp eip=%RGv\n", uErr, pvFault, (RTGCPTR)pRegFrame->rip));
206 STAM_PROFILE_START(&pVCpu->pgm.s.StatRZTrap0e, a);
207 STAM_STATS({ pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution) = NULL; } );
208
209 /* AMD uses the host's paging mode; Intel has a single mode (EPT). */
210 AssertMsg(enmShwPagingMode == PGMMODE_32_BIT || enmShwPagingMode == PGMMODE_PAE || enmShwPagingMode == PGMMODE_PAE_NX || enmShwPagingMode == PGMMODE_AMD64 || enmShwPagingMode == PGMMODE_AMD64_NX || enmShwPagingMode == PGMMODE_EPT, ("enmShwPagingMode=%d\n", enmShwPagingMode));
211
212#ifdef VBOX_WITH_STATISTICS
213 /*
214 * Error code stats.
215 */
216 if (uErr & X86_TRAP_PF_US)
217 {
218 if (!(uErr & X86_TRAP_PF_P))
219 {
220 if (uErr & X86_TRAP_PF_RW)
221 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSNotPresentWrite);
222 else
223 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSNotPresentRead);
224 }
225 else if (uErr & X86_TRAP_PF_RW)
226 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSWrite);
227 else if (uErr & X86_TRAP_PF_RSVD)
228 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSReserved);
229 else if (uErr & X86_TRAP_PF_ID)
230 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSNXE);
231 else
232 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eUSRead);
233 }
234 else
235 { /* Supervisor */
236 if (!(uErr & X86_TRAP_PF_P))
237 {
238 if (uErr & X86_TRAP_PF_RW)
239 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eSVNotPresentWrite);
240 else
241 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eSVNotPresentRead);
242 }
243 else if (uErr & X86_TRAP_PF_RW)
244 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eSVWrite);
245 else if (uErr & X86_TRAP_PF_ID)
246 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eSNXE);
247 else if (uErr & X86_TRAP_PF_RSVD)
248 STAM_COUNTER_INC(&pVCpu->pgm.s.StatRZTrap0eSVReserved);
249 }
250#endif
251
252 /*
253 * Call the worker.
254 *
255 * We pretend the guest is in protected mode without paging, so we can use existing code to build the
256 * nested page tables.
257 */
258 bool fLockTaken = false;
259 switch(enmShwPagingMode)
260 {
261 case PGMMODE_32_BIT:
262 rc = PGM_BTH_NAME_32BIT_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, pvFault, &fLockTaken);
263 break;
264 case PGMMODE_PAE:
265 case PGMMODE_PAE_NX:
266 rc = PGM_BTH_NAME_PAE_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, pvFault, &fLockTaken);
267 break;
268 case PGMMODE_AMD64:
269 case PGMMODE_AMD64_NX:
270 rc = PGM_BTH_NAME_AMD64_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, pvFault, &fLockTaken);
271 break;
272 case PGMMODE_EPT:
273 rc = PGM_BTH_NAME_EPT_PROT(Trap0eHandler)(pVCpu, uErr, pRegFrame, pvFault, &fLockTaken);
274 break;
275 default:
276 AssertFailed();
277 rc = VERR_INVALID_PARAMETER;
278 break;
279 }
280 if (fLockTaken)
281 {
282 Assert(PGMIsLockOwner(pVM));
283 pgmUnlock(pVM);
284 }
285 if (rc == VINF_PGM_SYNCPAGE_MODIFIED_PDE)
286 rc = VINF_SUCCESS;
287 else
288 /* Note: hack alert for difficult to reproduce problem. */
289 if ( rc == VERR_PAGE_NOT_PRESENT /* SMP only ; disassembly might fail. */
290 || rc == VERR_PAGE_TABLE_NOT_PRESENT /* seen with UNI & SMP */
291 || rc == VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT /* seen with SMP */
292 || rc == VERR_PAGE_MAP_LEVEL4_NOT_PRESENT) /* precaution */
293 {
294 Log(("WARNING: Unexpected VERR_PAGE_TABLE_NOT_PRESENT (%d) for page fault at %RGp error code %x (rip=%RGv)\n", rc, pvFault, uErr, pRegFrame->rip));
295 /* Some kind of inconsistency in the SMP case; it's safe to just execute the instruction again; not sure about single VCPU VMs though. */
296 rc = VINF_SUCCESS;
297 }
298
299 STAM_STATS({ if (!pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution))
300 pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution) = &pVCpu->pgm.s.StatRZTrap0eTime2Misc; });
301 STAM_PROFILE_STOP_EX(&pVCpu->pgm.s.StatRZTrap0e, pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution), a);
302 return rc;
303}
304
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