Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

wrtfault.c File Reference

#include "mi.h"

Go to the source code of this file.

Functions

NTSTATUS FASTCALL MiCopyOnWrite (IN PVOID FaultingAddress, IN PMMPTE PointerPte)


Function Documentation

NTSTATUS FASTCALL MiCopyOnWrite IN PVOID  FaultingAddress,
IN PMMPTE  PointerPte
 

Definition at line 25 of file wrtfault.c.

References ASSERT, _MMINFO_COUNTERS::CopyOnWriteCount, DbgPrint, _MMWSLE::e1, FALSE, _EPROCESS::ForkInProgress, KeFlushSingleTb(), LOCK_PFN, MI_CAPTURE_DIRTY_BIT_TO_PFN, MI_GET_PAGE_FRAME_FROM_PTE, MI_GET_SECONDARY_COLOR, MI_MAKE_PROTECT_NOT_WRITE_COPY, MI_PFN_ELEMENT, MI_SET_ACCESSED_IN_PTE, MI_SET_PTE_DIRTY, MI_WRITE_VALID_PTE_NEW_PROTECTION, MiDecrementCloneBlockReference(), MiDecrementShareCount(), MiEnsureAvailablePageOrWait(), MiFormatPfn(), MiFormatPte(), MiGetPteAddress, MiGetVirtualAddressMappedByPte, MiInitializeCopyOnWritePfn(), MiLocateCloneAddress, MiLocateWsle(), MiMapPageInHyperSpace(), MiReleasePageFileSpace(), MiRemoveAnyPage(), MiUnmapPageInHyperSpace, MiWaitForForkToComplete(), MM_DBG_PTE_UPDATE, MM_DBG_WRITEFAULT, MmInfoCounters, MmWorkingSetList, MmWsle, NULL, _EPROCESS::NumberOfPrivatePages, _MMPFN::OriginalPte, PAGE_SIZE, PERFINFO_PRIVATE_COPY_ON_WRITE, _MMWSLENTRY::Protection, PsGetCurrentProcess, PsGetCurrentThread, _MMPFN::PteAddress, _MMPFN::PteFrame, TRUE, _MMPTE::u, _MMPFN::u1, _MMPFN::u2, _MMPFN::u3, UNLOCK_PFN, WSLE_NULL_INDEX, and WSLE_NUMBER.

Referenced by MiProtectVirtualMemory(), MiSetProtectionOnSection(), and MmAccessFault().

00032 : 00033 00034 This routine performs a copy on write operation for the specified 00035 virtual address. 00036 00037 Arguments: 00038 00039 FaultingAddress - Supplies the virtual address which caused the 00040 fault. 00041 00042 PointerPte - Supplies the pointer to the PTE which caused the 00043 page fault. 00044 00045 00046 Return Value: 00047 00048 Returns the status of the fault handling operation. Can be one of: 00049 - Success. 00050 - In-page Error. 00051 00052 Environment: 00053 00054 Kernel mode, APC's disabled, Working set mutex held. 00055 00056 --*/ 00057 00058 { 00059 MMPTE TempPte; 00060 PFN_NUMBER PageFrameIndex; 00061 PFN_NUMBER NewPageIndex; 00062 PULONG CopyTo; 00063 PULONG CopyFrom; 00064 KIRQL OldIrql; 00065 PMMPFN Pfn1; 00066 // PMMPTE PointerPde; 00067 PEPROCESS CurrentProcess; 00068 PMMCLONE_BLOCK CloneBlock; 00069 PMMCLONE_DESCRIPTOR CloneDescriptor; 00070 PVOID VirtualAddress; 00071 WSLE_NUMBER WorkingSetIndex; 00072 LOGICAL FakeCopyOnWrite; 00073 00074 FakeCopyOnWrite = FALSE; 00075 00076 // 00077 // This is called from MmAccessFault, the PointerPte is valid 00078 // and the working set mutex ensures it cannot change state. 00079 // 00080 00081 #if DBG 00082 if (MmDebug & MM_DBG_WRITEFAULT) { 00083 DbgPrint("**copy on write Fault va %lx proc %lx thread %lx\n", 00084 (ULONG_PTR)FaultingAddress, 00085 (ULONG_PTR)PsGetCurrentProcess(), (ULONG_PTR)PsGetCurrentThread()); 00086 } 00087 00088 if (MmDebug & MM_DBG_PTE_UPDATE) { 00089 MiFormatPte(PointerPte); 00090 } 00091 #endif //DBG 00092 00093 ASSERT (PsGetCurrentProcess()->ForkInProgress == NULL); 00094 00095 // 00096 // Capture the PTE contents to TempPte. 00097 // 00098 00099 TempPte = *PointerPte; 00100 00101 // 00102 // Check to see if this is a prototype PTE with copy on write 00103 // enabled. 00104 // 00105 00106 if (TempPte.u.Hard.CopyOnWrite == 0) { 00107 00108 // 00109 // This is a fork page which is being made private in order 00110 // to change the protection of the page. 00111 // Do not make the page writable. 00112 // 00113 00114 FakeCopyOnWrite = TRUE; 00115 } 00116 00117 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte); 00118 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 00119 CurrentProcess = PsGetCurrentProcess(); 00120 00121 // 00122 // Acquire the PFN mutex. 00123 // 00124 00125 VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte); 00126 WorkingSetIndex = MiLocateWsle (VirtualAddress, MmWorkingSetList, 00127 Pfn1->u1.WsIndex); 00128 00129 LOCK_PFN (OldIrql); 00130 00131 // 00132 // The page must be copied into a new page. 00133 // 00134 00135 // 00136 // If a fork operation is in progress and the faulting thread 00137 // is not the thread performing the fork operation, block until 00138 // the fork is completed. 00139 // 00140 00141 if ((CurrentProcess->ForkInProgress != NULL) && 00142 (CurrentProcess->ForkInProgress != PsGetCurrentThread())) { 00143 MiWaitForForkToComplete (CurrentProcess); 00144 UNLOCK_PFN (OldIrql); 00145 return STATUS_SUCCESS; 00146 } 00147 00148 if (MiEnsureAvailablePageOrWait(CurrentProcess, NULL)) { 00149 00150 // 00151 // A wait operation was performed to obtain an available 00152 // page and the working set mutex and pfn mutexes have 00153 // been released and various things may have changed for 00154 // the worse. Rather than examine all the conditions again, 00155 // return and if things are still proper, the fault we 00156 // be taken again. 00157 // 00158 00159 UNLOCK_PFN (OldIrql); 00160 return STATUS_SUCCESS; 00161 } 00162 00163 // 00164 // Increment the number of private pages. 00165 // 00166 00167 CurrentProcess->NumberOfPrivatePages += 1; 00168 00169 MmInfoCounters.CopyOnWriteCount += 1; 00170 00171 // 00172 // A page is being copied and made private, the global state of 00173 // the shared page needs to be updated at this point on certain 00174 // hardware. This is done by ORing the dirty bit into the modify bit in 00175 // the PFN element. 00176 // 00177 00178 MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1); 00179 00180 // 00181 // This must be a prototype PTE. Perform the copy on write. 00182 // 00183 00184 #if DBG 00185 if (Pfn1->u3.e1.PrototypePte == 0) { 00186 DbgPrint("writefault - PTE indicates cow but not protopte\n"); 00187 MiFormatPte(PointerPte); 00188 MiFormatPfn(Pfn1); 00189 } 00190 #endif 00191 00192 CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress; 00193 00194 // 00195 // If the share count for the physical page is one, the reference 00196 // count is one, and the modified flag is clear the current page 00197 // can be stolen to satisfy the copy on write. 00198 // 00199 00200 #if 0 00201 // COMMENTED OUT **************************************************** 00202 // COMMENTED OUT **************************************************** 00203 // COMMENTED OUT **************************************************** 00204 if ((Pfn1->u2.ShareCount == 1) && (Pfn1->u3.e2.ReferenceCount == 1) 00205 && (Pfn1->u3.e1.Modified == 0)) { 00206 00207 // 00208 // Make this page a private page and return the prototype 00209 // PTE into its original contents. The PFN database for 00210 // this page now points to this PTE. 00211 // 00212 00213 // 00214 // Note that a page fault could occur referencing the prototype 00215 // PTE, so we map it into hyperspace to prevent a fault. 00216 // 00217 00218 MiRestorePrototypePte (Pfn1); 00219 00220 Pfn1->PteAddress = PointerPte; 00221 00222 // 00223 // Get the protection for the page. 00224 // 00225 00226 VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte); 00227 WorkingSetIndex = MiLocateWsle (VirtualAddress, MmWorkingSetList, 00228 Pfn1->u1.WsIndex); 00229 00230 ASSERT (WorkingSetIndex != WSLE_NULL_INDEX) { 00231 00232 Pfn1->OriginalPte.u.Long = 0; 00233 Pfn1->OriginalPte.u.Soft.Protection = 00234 MI_MAKE_PROTECT_NOT_WRITE_COPY ( 00235 MmWsle[WorkingSetIndex].u1.e1.Protection); 00236 00237 PointerPde = MiGetPteAddress(PointerPte); 00238 Pfn1->u3.e1.PrototypePte = 0; 00239 Pfn1->PteFrame = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde); 00240 00241 if (!FakeCopyOnWrite) { 00242 00243 // 00244 // If the page was Copy On Write and stolen or if the page was not 00245 // copy on write, update the PTE setting both the dirty bit and the 00246 // accessed bit. Note, that as this PTE is in the TB, the TB must 00247 // be flushed. 00248 // 00249 00250 MI_SET_PTE_DIRTY (TempPte); 00251 TempPte.u.Hard.Write = 1; 00252 MI_SET_ACCESSED_IN_PTE (&TempPte, 1); 00253 TempPte.u.Hard.CopyOnWrite = 0; 00254 MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte); 00255 00256 // 00257 // This is a copy on write operation, set the modify bit 00258 // in the PFN database and deallocate any page file space. 00259 // 00260 00261 Pfn1->u3.e1.Modified = 1; 00262 00263 if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 00264 (Pfn1->u3.e1.WriteInProgress == 0)) { 00265 00266 // 00267 // This page is in page file format, deallocate the page 00268 // file space. 00269 // 00270 00271 MiReleasePageFileSpace (Pfn1->OriginalPte); 00272 00273 // 00274 // Change original PTE to indicate no page file space is 00275 // reserved, otherwise the space will be deallocated when 00276 // the PTE is deleted. 00277 // 00278 00279 Pfn1->OriginalPte.u.Soft.PageFileHigh = 0; 00280 } 00281 } 00282 00283 // 00284 // The TB entry must be flushed as the valid PTE with the dirty 00285 // bit clear has been fetched into the TB. If it isn't flushed, 00286 // another fault is generated as the dirty bit is not set in 00287 // the cached TB entry. 00288 // 00289 00290 00291 KeFillEntryTb ((PHARDWARE_PTE)PointerPte, FaultingAddress, TRUE); 00292 00293 CloneDescriptor = MiLocateCloneAddress ((PVOID)CloneBlock); 00294 00295 if (CloneDescriptor != (PMMCLONE_DESCRIPTOR)NULL) { 00296 00297 // 00298 // Decrement the reference count for the clone block, 00299 // note that this could release and reacquire 00300 // the mutexes. 00301 // 00302 00303 MiDecrementCloneBlockReference ( CloneDescriptor, 00304 CloneBlock, 00305 CurrentProcess ); 00306 } 00307 00308 ] else [ 00309 00310 // ABOVE COMMENTED OUT **************************************************** 00311 // ABOVE COMMENTED OUT **************************************************** 00312 #endif 00313 00314 // 00315 // Get a new page with the same color as this page. 00316 // 00317 00318 NewPageIndex = MiRemoveAnyPage ( 00319 MI_GET_SECONDARY_COLOR (PageFrameIndex, 00320 Pfn1)); 00321 MiInitializeCopyOnWritePfn (NewPageIndex, PointerPte, WorkingSetIndex, NULL); 00322 00323 UNLOCK_PFN (OldIrql); 00324 00325 CopyTo = (PULONG)MiMapPageInHyperSpace (NewPageIndex, &OldIrql); 00326 00327 #if defined(_MIALT4K_) 00328 00329 // 00330 // Should avoid accessing the user space. Accessing the user space may potentially 00331 // cause a page fault on the alternate table. 00332 // 00333 00334 CopyFrom = KSEG_ADDRESS(PointerPte->u.Hard.PageFrameNumber); 00335 00336 #else 00337 CopyFrom = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte); 00338 #endif 00339 00340 RtlCopyMemory ( CopyTo, CopyFrom, PAGE_SIZE); 00341 00342 PERFINFO_PRIVATE_COPY_ON_WRITE(CopyFrom, PAGE_SIZE); 00343 00344 MiUnmapPageInHyperSpace (OldIrql); 00345 00346 if (!FakeCopyOnWrite) { 00347 00348 // 00349 // If the page was really a copy on write page, make it 00350 // accessed, dirty and writable. Also, clear the copy-on-write 00351 // bit in the PTE. 00352 // 00353 00354 MI_SET_PTE_DIRTY (TempPte); 00355 TempPte.u.Hard.Write = 1; 00356 MI_SET_ACCESSED_IN_PTE (&TempPte, 1); 00357 TempPte.u.Hard.CopyOnWrite = 0; 00358 TempPte.u.Hard.PageFrameNumber = NewPageIndex; 00359 00360 } else { 00361 00362 // 00363 // The page was not really a copy on write, just change 00364 // the frame field of the PTE. 00365 // 00366 00367 TempPte.u.Hard.PageFrameNumber = NewPageIndex; 00368 } 00369 00370 // 00371 // If the modify bit is set in the PFN database for the 00372 // page, the data cache must be flushed. This is due to the 00373 // fact that this process may have been cloned and the cache 00374 // still contains stale data destined for the page we are 00375 // going to remove. 00376 // 00377 00378 ASSERT (TempPte.u.Hard.Valid == 1); 00379 00380 LOCK_PFN (OldIrql); 00381 00382 // 00383 // Flush the TB entry for this page. 00384 // 00385 00386 KeFlushSingleTb (FaultingAddress, 00387 TRUE, 00388 FALSE, 00389 (PHARDWARE_PTE)PointerPte, 00390 TempPte.u.Flush); 00391 00392 // 00393 // Decrement the share count for the page which was copied 00394 // as this pte no longer refers to it. 00395 // 00396 00397 MiDecrementShareCount (PageFrameIndex); 00398 00399 CloneDescriptor = MiLocateCloneAddress ((PVOID)CloneBlock); 00400 00401 if (CloneDescriptor != (PMMCLONE_DESCRIPTOR)NULL) { 00402 00403 // 00404 // Decrement the reference count for the clone block, 00405 // note that this could release and reacquire 00406 // the mutexes. 00407 // 00408 00409 MiDecrementCloneBlockReference ( CloneDescriptor, 00410 CloneBlock, 00411 CurrentProcess ); 00412 } 00413 00414 UNLOCK_PFN (OldIrql); 00415 return STATUS_SUCCESS; 00416 } }


Generated on Sat May 15 19:46:10 2004 for test by doxygen 1.3.7