VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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