Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpagfault.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: ntoskrnl/mm/ARM3/pagfault.c 00005 * PURPOSE: ARM Memory Manager Page Fault Handling 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #define MODULE_INVOLVED_IN_ARM3 00016 #include "../ARM3/miarm.h" 00017 00018 /* GLOBALS ********************************************************************/ 00019 00020 #if MI_TRACE_PFNS 00021 BOOLEAN UserPdeFault = FALSE; 00022 #endif 00023 00024 /* PRIVATE FUNCTIONS **********************************************************/ 00025 00026 PMMPTE 00027 NTAPI 00028 MiCheckVirtualAddress(IN PVOID VirtualAddress, 00029 OUT PULONG ProtectCode, 00030 OUT PMMVAD *ProtoVad) 00031 { 00032 PMMVAD Vad; 00033 PMMPTE PointerPte; 00034 00035 /* No prototype/section support for now */ 00036 *ProtoVad = NULL; 00037 00038 /* Check if this is a page table address */ 00039 if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress)) 00040 { 00041 /* This should never happen, as these addresses are handled by the double-maping */ 00042 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) && 00043 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool)) 00044 { 00045 /* Fail such access */ 00046 *ProtectCode = MM_NOACCESS; 00047 return NULL; 00048 } 00049 00050 /* Return full access rights */ 00051 *ProtectCode = MM_READWRITE; 00052 return NULL; 00053 } 00054 00055 /* Should not be a session address */ 00056 ASSERT(MI_IS_SESSION_ADDRESS(VirtualAddress) == FALSE); 00057 00058 /* Special case for shared data */ 00059 if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA) 00060 { 00061 /* It's a read-only page */ 00062 *ProtectCode = MM_READONLY; 00063 return MmSharedUserDataPte; 00064 } 00065 00066 /* Find the VAD, it might not exist if the address is bogus */ 00067 Vad = MiLocateAddress(VirtualAddress); 00068 if (!Vad) 00069 { 00070 /* Bogus virtual address */ 00071 *ProtectCode = MM_NOACCESS; 00072 return NULL; 00073 } 00074 00075 /* This must be a VM VAD */ 00076 ASSERT(Vad->u.VadFlags.VadType == VadNone); 00077 00078 /* Check if it's a section, or just an allocation */ 00079 if (Vad->u.VadFlags.PrivateMemory) 00080 { 00081 /* This must be a TEB/PEB VAD */ 00082 if (Vad->u.VadFlags.MemCommit) 00083 { 00084 /* It's committed, so return the VAD protection */ 00085 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection; 00086 } 00087 else 00088 { 00089 /* It has not yet been committed, so return no access */ 00090 *ProtectCode = MM_NOACCESS; 00091 } 00092 return NULL; 00093 } 00094 else 00095 { 00096 /* Return the proto VAD */ 00097 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0); 00098 *ProtoVad = Vad; 00099 00100 /* Get the prototype PTE for this page */ 00101 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte; 00102 ASSERT(PointerPte <= Vad->LastContiguousPte); 00103 ASSERT(PointerPte != NULL); 00104 00105 /* Return the Prototype PTE and the protection for the page mapping */ 00106 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection; 00107 return PointerPte; 00108 } 00109 } 00110 00111 #if (_MI_PAGING_LEVELS == 2) 00112 BOOLEAN 00113 FORCEINLINE 00114 MiSynchronizeSystemPde(PMMPDE PointerPde) 00115 { 00116 MMPDE SystemPde; 00117 ULONG Index; 00118 00119 /* Get the Index from the PDE */ 00120 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE); 00121 00122 /* Copy the PDE from the double-mapped system page directory */ 00123 SystemPde = MmSystemPagePtes[Index]; 00124 *PointerPde = SystemPde; 00125 00126 /* Make sure we re-read the PDE and PTE */ 00127 KeMemoryBarrierWithoutFence(); 00128 00129 /* Return, if we had success */ 00130 return (BOOLEAN)SystemPde.u.Hard.Valid; 00131 } 00132 00133 NTSTATUS 00134 FASTCALL 00135 MiCheckPdeForPagedPool(IN PVOID Address) 00136 { 00137 PMMPDE PointerPde; 00138 NTSTATUS Status = STATUS_SUCCESS; 00139 00140 /* No session support in ReactOS yet */ 00141 ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE); 00142 ASSERT(MI_IS_SESSION_PTE(Address) == FALSE); 00143 00144 // 00145 // Check if this is a fault while trying to access the page table itself 00146 // 00147 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) 00148 { 00149 // 00150 // Send a hint to the page fault handler that this is only a valid fault 00151 // if we already detected this was access within the page table range 00152 // 00153 PointerPde = (PMMPDE)MiAddressToPte(Address); 00154 Status = STATUS_WAIT_1; 00155 } 00156 else if (Address < MmSystemRangeStart) 00157 { 00158 // 00159 // This is totally illegal 00160 // 00161 return STATUS_ACCESS_VIOLATION; 00162 } 00163 else 00164 { 00165 // 00166 // Get the PDE for the address 00167 // 00168 PointerPde = MiAddressToPde(Address); 00169 } 00170 00171 // 00172 // Check if it's not valid 00173 // 00174 if (PointerPde->u.Hard.Valid == 0) 00175 { 00176 #ifdef _M_AMD64 00177 ASSERT(FALSE); 00178 #else 00179 // 00180 // Copy it from our double-mapped system page directory 00181 // 00182 InterlockedExchangePte(PointerPde, 00183 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long); 00184 #endif 00185 } 00186 00187 // 00188 // Return status 00189 // 00190 return Status; 00191 } 00192 #else 00193 NTSTATUS 00194 FASTCALL 00195 MiCheckPdeForPagedPool(IN PVOID Address) 00196 { 00197 return STATUS_ACCESS_VIOLATION; 00198 } 00199 #endif 00200 00201 VOID 00202 NTAPI 00203 MiZeroPfn(IN PFN_NUMBER PageFrameNumber) 00204 { 00205 PMMPTE ZeroPte; 00206 MMPTE TempPte; 00207 PMMPFN Pfn1; 00208 PVOID ZeroAddress; 00209 00210 /* Get the PFN for this page */ 00211 Pfn1 = MiGetPfnEntry(PageFrameNumber); 00212 ASSERT(Pfn1); 00213 00214 /* Grab a system PTE we can use to zero the page */ 00215 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace); 00216 ASSERT(ZeroPte); 00217 00218 /* Initialize the PTE for it */ 00219 TempPte = ValidKernelPte; 00220 TempPte.u.Hard.PageFrameNumber = PageFrameNumber; 00221 00222 /* Setup caching */ 00223 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined) 00224 { 00225 /* Write combining, no caching */ 00226 MI_PAGE_DISABLE_CACHE(&TempPte); 00227 MI_PAGE_WRITE_COMBINED(&TempPte); 00228 } 00229 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached) 00230 { 00231 /* Write through, no caching */ 00232 MI_PAGE_DISABLE_CACHE(&TempPte); 00233 MI_PAGE_WRITE_THROUGH(&TempPte); 00234 } 00235 00236 /* Make the system PTE valid with our PFN */ 00237 MI_WRITE_VALID_PTE(ZeroPte, TempPte); 00238 00239 /* Get the address it maps to, and zero it out */ 00240 ZeroAddress = MiPteToAddress(ZeroPte); 00241 KeZeroPages(ZeroAddress, PAGE_SIZE); 00242 00243 /* Now get rid of it */ 00244 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace); 00245 } 00246 00247 NTSTATUS 00248 NTAPI 00249 MiResolveDemandZeroFault(IN PVOID Address, 00250 IN PMMPTE PointerPte, 00251 IN PEPROCESS Process, 00252 IN KIRQL OldIrql) 00253 { 00254 PFN_NUMBER PageFrameNumber = 0; 00255 MMPTE TempPte; 00256 BOOLEAN NeedZero = FALSE, HaveLock = FALSE; 00257 ULONG Color; 00258 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n", 00259 Address, 00260 Process); 00261 00262 /* Must currently only be called by paging path */ 00263 if ((Process) && (OldIrql == MM_NOIRQL)) 00264 { 00265 /* Sanity check */ 00266 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte)); 00267 00268 /* No forking yet */ 00269 ASSERT(Process->ForkInProgress == NULL); 00270 00271 /* Get process color */ 00272 Color = MI_GET_NEXT_PROCESS_COLOR(Process); 00273 ASSERT(Color != 0xFFFFFFFF); 00274 00275 /* We'll need a zero page */ 00276 NeedZero = TRUE; 00277 } 00278 else 00279 { 00280 /* Check if we need a zero page */ 00281 NeedZero = (OldIrql != MM_NOIRQL); 00282 00283 /* Get the next system page color */ 00284 Color = MI_GET_NEXT_COLOR(); 00285 } 00286 00287 /* Check if the PFN database should be acquired */ 00288 if (OldIrql == MM_NOIRQL) 00289 { 00290 /* Acquire it and remember we should release it after */ 00291 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00292 HaveLock = TRUE; 00293 } 00294 00295 /* We either manually locked the PFN DB, or already came with it locked */ 00296 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00297 00298 /* Do we need a zero page? */ 00299 ASSERT(PointerPte->u.Hard.Valid == 0); 00300 #if MI_TRACE_PFNS 00301 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE); 00302 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO); 00303 #endif 00304 if (Process) MI_SET_PROCESS2(Process->ImageFileName); 00305 if (!Process) MI_SET_PROCESS2("Kernel Demand 0"); 00306 if ((NeedZero) && (Process)) 00307 { 00308 /* Try to get one, if we couldn't grab a free page and zero it */ 00309 PageFrameNumber = MiRemoveZeroPageSafe(Color); 00310 if (PageFrameNumber) 00311 { 00312 /* We got a genuine zero page, stop worrying about it */ 00313 NeedZero = FALSE; 00314 } 00315 else 00316 { 00317 /* We'll need a free page and zero it manually */ 00318 PageFrameNumber = MiRemoveAnyPage(Color); 00319 } 00320 } 00321 else if (!NeedZero) 00322 { 00323 /* Process or system doesn't want a zero page, grab anything */ 00324 PageFrameNumber = MiRemoveAnyPage(Color); 00325 } 00326 else 00327 { 00328 /* System wants a zero page, obtain one */ 00329 PageFrameNumber = MiRemoveZeroPage(Color); 00330 NeedZero = FALSE; 00331 } 00332 00333 /* Initialize it */ 00334 MiInitializePfn(PageFrameNumber, PointerPte, TRUE); 00335 00336 /* Increment demand zero faults */ 00337 KeGetCurrentPrcb()->MmDemandZeroCount++; 00338 00339 /* Release PFN lock if needed */ 00340 if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00341 00342 /* Zero the page if need be */ 00343 if (NeedZero) MiZeroPfn(PageFrameNumber); 00344 00345 /* Fault on user PDE, or fault on user PTE? */ 00346 if (PointerPte <= MiHighestUserPte) 00347 { 00348 /* User fault, build a user PTE */ 00349 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 00350 PointerPte, 00351 PointerPte->u.Soft.Protection, 00352 PageFrameNumber); 00353 } 00354 else 00355 { 00356 /* This is a user-mode PDE, create a kernel PTE for it */ 00357 MI_MAKE_HARDWARE_PTE(&TempPte, 00358 PointerPte, 00359 PointerPte->u.Soft.Protection, 00360 PageFrameNumber); 00361 } 00362 00363 /* Set it dirty if it's a writable page */ 00364 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 00365 00366 /* Write it */ 00367 MI_WRITE_VALID_PTE(PointerPte, TempPte); 00368 00369 // 00370 // It's all good now 00371 // 00372 DPRINT("Demand zero page has now been paged in\n"); 00373 return STATUS_PAGE_FAULT_DEMAND_ZERO; 00374 } 00375 00376 NTSTATUS 00377 NTAPI 00378 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction, 00379 IN PVOID Address, 00380 IN PMMPTE PointerPte, 00381 IN PMMPTE PointerProtoPte, 00382 IN KIRQL OldIrql, 00383 IN PMMPFN Pfn1) 00384 { 00385 MMPTE TempPte; 00386 PMMPTE OriginalPte, PageTablePte; 00387 ULONG_PTR Protection; 00388 PFN_NUMBER PageFrameIndex; 00389 PMMPFN Pfn2; 00390 00391 /* Must be called with an valid prototype PTE, with the PFN lock held */ 00392 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00393 ASSERT(PointerProtoPte->u.Hard.Valid == 1); 00394 00395 /* Get the page */ 00396 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte); 00397 00398 /* Get the PFN entry and set it as a prototype PTE */ 00399 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00400 Pfn1->u3.e1.PrototypePte = 1; 00401 00402 /* Increment the share count for the page table */ 00403 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs. 00404 // This could be beause MiDeletePte is now being called from strange code in Rosmm 00405 PageTablePte = MiAddressToPte(PointerPte); 00406 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber); 00407 //Pfn2->u2.ShareCount++; 00408 00409 /* Check where we should be getting the protection information from */ 00410 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 00411 { 00412 /* Get the protection from the PTE, there's no real Proto PTE data */ 00413 Protection = PointerPte->u.Soft.Protection; 00414 } 00415 else 00416 { 00417 /* Get the protection from the original PTE link */ 00418 OriginalPte = &Pfn1->OriginalPte; 00419 Protection = OriginalPte->u.Soft.Protection; 00420 } 00421 00422 /* Release the PFN lock */ 00423 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00424 00425 /* Remove caching bits */ 00426 Protection &= ~(MM_NOCACHE | MM_NOACCESS); 00427 00428 /* Check if this is a kernel or user address */ 00429 if (Address < MmSystemRangeStart) 00430 { 00431 /* Build the user PTE */ 00432 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex); 00433 } 00434 else 00435 { 00436 /* Build the kernel PTE */ 00437 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex); 00438 } 00439 00440 /* Write the PTE */ 00441 MI_WRITE_VALID_PTE(PointerPte, TempPte); 00442 00443 /* Return success */ 00444 return STATUS_SUCCESS; 00445 } 00446 00447 NTSTATUS 00448 NTAPI 00449 MiResolveTransitionFault(IN PVOID FaultingAddress, 00450 IN PMMPTE PointerPte, 00451 IN PEPROCESS CurrentProcess, 00452 IN KIRQL OldIrql, 00453 OUT PVOID *InPageBlock) 00454 { 00455 PFN_NUMBER PageFrameIndex; 00456 PMMPFN Pfn1; 00457 MMPTE TempPte; 00458 PMMPTE PointerToPteForProtoPage; 00459 USHORT NewRefCount; 00460 DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName); 00461 00462 /* Windowss does this check */ 00463 ASSERT(*InPageBlock == NULL); 00464 00465 /* ARM3 doesn't support this path */ 00466 ASSERT(OldIrql != MM_NOIRQL); 00467 00468 /* Capture the PTE and make sure it's in transition format */ 00469 TempPte = *PointerPte; 00470 ASSERT((TempPte.u.Soft.Valid == 0) && 00471 (TempPte.u.Soft.Prototype == 0) && 00472 (TempPte.u.Soft.Transition == 1)); 00473 00474 /* Get the PFN and the PFN entry */ 00475 PageFrameIndex = TempPte.u.Trans.PageFrameNumber; 00476 DPRINT1("Transition PFN: %lx\n", PageFrameIndex); 00477 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00478 00479 /* One more transition fault! */ 00480 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 00481 00482 /* This is from ARM3 -- Windows normally handles this here */ 00483 ASSERT(Pfn1->u4.InPageError == 0); 00484 00485 /* Not supported in ARM3 */ 00486 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 00487 00488 /* Windows checks there's some free pages and this isn't an in-page error */ 00489 ASSERT(MmAvailablePages >= 0); 00490 ASSERT(Pfn1->u4.InPageError == 0); 00491 00492 /* Was this a transition page in the valid list, or free/zero list? */ 00493 if (Pfn1->u3.e1.PageLocation == ActiveAndValid) 00494 { 00495 /* All Windows does here is a bunch of sanity checks */ 00496 DPRINT1("Transition in active list\n"); 00497 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) && 00498 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd))); 00499 ASSERT(Pfn1->u2.ShareCount != 0); 00500 ASSERT(Pfn1->u3.e2.ReferenceCount != 0); 00501 } 00502 else 00503 { 00504 /* Otherwise, the page is removed from its list */ 00505 DPRINT1("Transition page in free/zero list\n"); 00506 MiUnlinkPageFromList(Pfn1); 00507 00508 /* Windows does these checks -- perhaps a macro? */ 00509 ASSERT(Pfn1->u2.ShareCount == 0); 00510 ASSERT(Pfn1->u2.ShareCount == 0); 00511 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); 00512 00513 /* Check if this was a prototype PTE */ 00514 if ((Pfn1->u3.e1.PrototypePte == 1) && 00515 (Pfn1->OriginalPte.u.Soft.Prototype == 1)) 00516 { 00517 DPRINT1("Prototype floating page not yet supported\n"); 00518 ASSERT(FALSE); 00519 } 00520 00521 /* FIXME: Update counter */ 00522 00523 /* We must be the first reference */ 00524 NewRefCount = InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); 00525 ASSERT(NewRefCount == 1); 00526 } 00527 00528 /* At this point, there should no longer be any in-page errors */ 00529 ASSERT(Pfn1->u4.InPageError == 0); 00530 00531 /* Check if this was a PFN with no more share references */ 00532 if (Pfn1->u2.ShareCount == 0) 00533 { 00534 /* Windows checks for these... maybe a macro? */ 00535 ASSERT(Pfn1->u3.e2.ReferenceCount != 0); 00536 ASSERT(Pfn1->u2.ShareCount == 0); 00537 00538 /* Was this the last active reference to it */ 00539 DPRINT1("Page share count is zero\n"); 00540 if (Pfn1->u3.e2.ReferenceCount == 1) 00541 { 00542 /* The page should be leaking somewhere on the free/zero list */ 00543 DPRINT1("Page reference count is one\n"); 00544 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); 00545 if ((Pfn1->u3.e1.PrototypePte == 1) && 00546 (Pfn1->OriginalPte.u.Soft.Prototype == 1)) 00547 { 00548 /* Do extra processing if it was a prototype page */ 00549 DPRINT1("Prototype floating page not yet supported\n"); 00550 ASSERT(FALSE); 00551 } 00552 00553 /* FIXME: Update counter */ 00554 } 00555 } 00556 00557 /* Bump the share count and make the page valid */ 00558 Pfn1->u2.ShareCount++; 00559 Pfn1->u3.e1.PageLocation = ActiveAndValid; 00560 00561 /* Prototype PTEs are in paged pool, which itself might be in transition */ 00562 if (FaultingAddress >= MmSystemRangeStart) 00563 { 00564 /* Check if this is a paged pool PTE in transition state */ 00565 PointerToPteForProtoPage = MiAddressToPte(PointerPte); 00566 TempPte = *PointerToPteForProtoPage; 00567 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1)) 00568 { 00569 /* This isn't yet supported */ 00570 DPRINT1("Double transition fault not yet supported\n"); 00571 ASSERT(FALSE); 00572 } 00573 } 00574 00575 /* Build the transition PTE -- maybe a macro? */ 00576 ASSERT(PointerPte->u.Hard.Valid == 0); 00577 ASSERT(PointerPte->u.Trans.Prototype == 0); 00578 ASSERT(PointerPte->u.Trans.Transition == 1); 00579 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) | 00580 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) | 00581 MiDetermineUserGlobalPteMask(PointerPte); 00582 00583 /* FIXME: Set dirty bit */ 00584 00585 /* Write the valid PTE */ 00586 MI_WRITE_VALID_PTE(PointerPte, TempPte); 00587 00588 /* Return success */ 00589 return STATUS_PAGE_FAULT_TRANSITION; 00590 } 00591 00592 NTSTATUS 00593 NTAPI 00594 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction, 00595 IN PVOID Address, 00596 IN PMMPTE PointerPte, 00597 IN PMMPTE PointerProtoPte, 00598 IN OUT PMMPFN *OutPfn, 00599 OUT PVOID *PageFileData, 00600 OUT PMMPTE PteValue, 00601 IN PEPROCESS Process, 00602 IN KIRQL OldIrql, 00603 IN PVOID TrapInformation) 00604 { 00605 MMPTE TempPte, PteContents; 00606 PMMPFN Pfn1; 00607 PFN_NUMBER PageFrameIndex; 00608 NTSTATUS Status; 00609 PVOID InPageBlock = NULL; 00610 00611 /* Must be called with an invalid, prototype PTE, with the PFN lock held */ 00612 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00613 ASSERT(PointerPte->u.Hard.Valid == 0); 00614 ASSERT(PointerPte->u.Soft.Prototype == 1); 00615 00616 /* Read the prototype PTE and check if it's valid */ 00617 TempPte = *PointerProtoPte; 00618 if (TempPte.u.Hard.Valid == 1) 00619 { 00620 /* One more user of this mapped page */ 00621 PageFrameIndex = PFN_FROM_PTE(&TempPte); 00622 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00623 Pfn1->u2.ShareCount++; 00624 00625 /* Call it a transition */ 00626 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); 00627 00628 /* Complete the prototype PTE fault -- this will release the PFN lock */ 00629 return MiCompleteProtoPteFault(StoreInstruction, 00630 Address, 00631 PointerPte, 00632 PointerProtoPte, 00633 OldIrql, 00634 NULL); 00635 } 00636 00637 /* Make sure there's some protection mask */ 00638 if (TempPte.u.Long == 0) 00639 { 00640 /* Release the lock */ 00641 DPRINT1("Access on reserved section?\n"); 00642 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00643 return STATUS_ACCESS_VIOLATION; 00644 } 00645 00646 /* Check for access rights on the PTE proper */ 00647 PteContents = *PointerPte; 00648 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) 00649 { 00650 if (!PteContents.u.Proto.ReadOnly) 00651 { 00652 /* FIXME: CHECK FOR ACCESS AND COW */ 00653 } 00654 } 00655 else 00656 { 00657 /* FIXME: Should check for COW */ 00658 } 00659 00660 /* Check for clone PTEs */ 00661 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL); 00662 00663 /* We don't support mapped files yet */ 00664 ASSERT(TempPte.u.Soft.Prototype == 0); 00665 00666 /* We might however have transition PTEs */ 00667 if (TempPte.u.Soft.Transition == 1) 00668 { 00669 /* Resolve the transition fault */ 00670 ASSERT(OldIrql != MM_NOIRQL); 00671 Status = MiResolveTransitionFault(Address, 00672 PointerProtoPte, 00673 Process, 00674 OldIrql, 00675 &InPageBlock); 00676 ASSERT(NT_SUCCESS(Status)); 00677 } 00678 else 00679 { 00680 /* We also don't support paged out pages */ 00681 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 00682 00683 /* Resolve the demand zero fault */ 00684 Status = MiResolveDemandZeroFault(Address, 00685 PointerProtoPte, 00686 Process, 00687 OldIrql); 00688 ASSERT(NT_SUCCESS(Status)); 00689 } 00690 00691 /* Complete the prototype PTE fault -- this will release the PFN lock */ 00692 ASSERT(PointerPte->u.Hard.Valid == 0); 00693 return MiCompleteProtoPteFault(StoreInstruction, 00694 Address, 00695 PointerPte, 00696 PointerProtoPte, 00697 OldIrql, 00698 NULL); 00699 } 00700 00701 NTSTATUS 00702 NTAPI 00703 MiDispatchFault(IN BOOLEAN StoreInstruction, 00704 IN PVOID Address, 00705 IN PMMPTE PointerPte, 00706 IN PMMPTE PointerProtoPte, 00707 IN BOOLEAN Recursive, 00708 IN PEPROCESS Process, 00709 IN PVOID TrapInformation, 00710 IN PVOID Vad) 00711 { 00712 MMPTE TempPte; 00713 KIRQL OldIrql, LockIrql; 00714 NTSTATUS Status; 00715 PMMPTE SuperProtoPte; 00716 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n", 00717 Address, 00718 Process); 00719 00720 /* Make sure the addresses are ok */ 00721 ASSERT(PointerPte == MiAddressToPte(Address)); 00722 00723 // 00724 // Make sure APCs are off and we're not at dispatch 00725 // 00726 OldIrql = KeGetCurrentIrql(); 00727 ASSERT(OldIrql <= APC_LEVEL); 00728 ASSERT(KeAreAllApcsDisabled() == TRUE); 00729 00730 // 00731 // Grab a copy of the PTE 00732 // 00733 TempPte = *PointerPte; 00734 00735 /* Do we have a prototype PTE? */ 00736 if (PointerProtoPte) 00737 { 00738 /* This should never happen */ 00739 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte)); 00740 00741 /* Check if this is a kernel-mode address */ 00742 SuperProtoPte = MiAddressToPte(PointerProtoPte); 00743 if (Address >= MmSystemRangeStart) 00744 { 00745 /* Lock the PFN database */ 00746 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00747 00748 /* Has the PTE been made valid yet? */ 00749 if (!SuperProtoPte->u.Hard.Valid) 00750 { 00751 UNIMPLEMENTED; 00752 while (TRUE); 00753 } 00754 else 00755 { 00756 /* Resolve the fault -- this will release the PFN lock */ 00757 ASSERT(PointerPte->u.Hard.Valid == 0); 00758 Status = MiResolveProtoPteFault(StoreInstruction, 00759 Address, 00760 PointerPte, 00761 PointerProtoPte, 00762 NULL, 00763 NULL, 00764 NULL, 00765 Process, 00766 LockIrql, 00767 TrapInformation); 00768 ASSERT(Status == STATUS_SUCCESS); 00769 00770 /* Complete this as a transition fault */ 00771 ASSERT(OldIrql == KeGetCurrentIrql()); 00772 ASSERT(OldIrql <= APC_LEVEL); 00773 ASSERT(KeAreAllApcsDisabled() == TRUE); 00774 return Status; 00775 } 00776 } 00777 else 00778 { 00779 /* We currently only handle very limited paths */ 00780 ASSERT(PointerPte->u.Soft.Prototype == 1); 00781 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED); 00782 00783 /* Lock the PFN database */ 00784 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00785 00786 /* For our current usage, this should be true */ 00787 ASSERT(SuperProtoPte->u.Hard.Valid == 1); 00788 ASSERT(TempPte.u.Hard.Valid == 0); 00789 00790 /* Resolve the fault -- this will release the PFN lock */ 00791 Status = MiResolveProtoPteFault(StoreInstruction, 00792 Address, 00793 PointerPte, 00794 PointerProtoPte, 00795 NULL, 00796 NULL, 00797 NULL, 00798 Process, 00799 LockIrql, 00800 TrapInformation); 00801 ASSERT(Status == STATUS_SUCCESS); 00802 00803 /* Complete this as a transition fault */ 00804 ASSERT(OldIrql == KeGetCurrentIrql()); 00805 ASSERT(OldIrql <= APC_LEVEL); 00806 ASSERT(KeAreAllApcsDisabled() == TRUE); 00807 return STATUS_PAGE_FAULT_TRANSITION; 00808 } 00809 } 00810 00811 // 00812 // The PTE must be invalid but not completely empty. It must also not be a 00813 // prototype PTE as that scenario should've been handled above 00814 // 00815 ASSERT(TempPte.u.Hard.Valid == 0); 00816 ASSERT(TempPte.u.Soft.Prototype == 0); 00817 ASSERT(TempPte.u.Long != 0); 00818 00819 // 00820 // No transition or page file software PTEs in ARM3 yet, so this must be a 00821 // demand zero page 00822 // 00823 ASSERT(TempPte.u.Soft.Transition == 0); 00824 ASSERT(TempPte.u.Soft.PageFileHigh == 0); 00825 00826 // 00827 // If we got this far, the PTE can only be a demand zero PTE, which is what 00828 // we want. Go handle it! 00829 // 00830 Status = MiResolveDemandZeroFault(Address, 00831 PointerPte, 00832 Process, 00833 MM_NOIRQL); 00834 ASSERT(KeAreAllApcsDisabled() == TRUE); 00835 if (NT_SUCCESS(Status)) 00836 { 00837 // 00838 // Make sure we're returning in a sane state and pass the status down 00839 // 00840 ASSERT(OldIrql == KeGetCurrentIrql()); 00841 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 00842 return Status; 00843 } 00844 00845 // 00846 // Generate an access fault 00847 // 00848 return STATUS_ACCESS_VIOLATION; 00849 } 00850 00851 NTSTATUS 00852 NTAPI 00853 MmArmAccessFault(IN BOOLEAN StoreInstruction, 00854 IN PVOID Address, 00855 IN KPROCESSOR_MODE Mode, 00856 IN PVOID TrapInformation) 00857 { 00858 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; 00859 PMMPTE ProtoPte = NULL; 00860 PMMPTE PointerPte = MiAddressToPte(Address); 00861 PMMPDE PointerPde = MiAddressToPde(Address); 00862 #if (_MI_PAGING_LEVELS >= 3) 00863 PMMPDE PointerPpe = MiAddressToPpe(Address); 00864 #if (_MI_PAGING_LEVELS == 4) 00865 PMMPDE PointerPxe = MiAddressToPxe(Address); 00866 #endif 00867 #endif 00868 MMPTE TempPte; 00869 PETHREAD CurrentThread; 00870 PEPROCESS CurrentProcess; 00871 NTSTATUS Status; 00872 PMMSUPPORT WorkingSet; 00873 ULONG ProtectionCode; 00874 PMMVAD Vad; 00875 PFN_NUMBER PageFrameIndex; 00876 ULONG Color; 00877 DPRINT("ARM3 FAULT AT: %p\n", Address); 00878 00879 /* Check for page fault on high IRQL */ 00880 if (OldIrql > APC_LEVEL) 00881 { 00882 // There are some special cases where this is okay, but not in ARM3 yet 00883 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n", 00884 Address, 00885 OldIrql); 00886 ASSERT(OldIrql <= APC_LEVEL); 00887 } 00888 00889 /* Check for kernel fault address */ 00890 if (Address >= MmSystemRangeStart) 00891 { 00892 /* Bail out, if the fault came from user mode */ 00893 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION; 00894 00895 /* PXEs and PPEs for kernel mode are mapped for everything we need */ 00896 #if (_MI_PAGING_LEVELS >= 3) 00897 if ( 00898 #if (_MI_PAGING_LEVELS == 4) 00899 (PointerPxe->u.Hard.Valid == 0) || 00900 #endif 00901 (PointerPpe->u.Hard.Valid == 0)) 00902 { 00903 /* The address is not from any pageable area! */ 00904 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 00905 (ULONG_PTR)Address, 00906 StoreInstruction, 00907 (ULONG_PTR)TrapInformation, 00908 2); 00909 } 00910 #endif 00911 00912 #if (_MI_PAGING_LEVELS == 2) 00913 /* Check if we have a situation that might need synchronization 00914 of the PDE with the system page directory */ 00915 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) 00916 { 00917 /* This could be a paged pool commit with an unsychronized PDE. 00918 NOTE: This way it works on x86, verify for other architectures! */ 00919 if (MiSynchronizeSystemPde((PMMPDE)PointerPte)) return STATUS_SUCCESS; 00920 } 00921 #endif 00922 00923 /* Check if the PDE is invalid */ 00924 if (PointerPde->u.Hard.Valid == 0) 00925 { 00926 #if (_MI_PAGING_LEVELS == 2) 00927 /* Sync this PDE and check, if that made it valid */ 00928 if (!MiSynchronizeSystemPde(PointerPde)) 00929 #endif 00930 { 00931 /* PDE (still) not valid, kill the system */ 00932 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 00933 (ULONG_PTR)Address, 00934 StoreInstruction, 00935 (ULONG_PTR)TrapInformation, 00936 2); 00937 } 00938 } 00939 00940 /* The PDE is valid, so read the PTE */ 00941 TempPte = *PointerPte; 00942 if (TempPte.u.Hard.Valid == 1) 00943 { 00944 // 00945 // Only two things can go wrong here: 00946 // Executing NX page (we couldn't care less) 00947 // Writing to a read-only page (the stuff ARM3 works with is write, 00948 // so again, moot point). 00949 // 00950 00951 // 00952 // Otherwise, the PDE was probably invalid, and all is good now 00953 // 00954 return STATUS_SUCCESS; 00955 } 00956 00957 /* Get the current thread */ 00958 CurrentThread = PsGetCurrentThread(); 00959 00960 /* Check for a fault on the page table or hyperspace */ 00961 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address)) goto UserFault; 00962 00963 /* Use the system working set */ 00964 WorkingSet = &MmSystemCacheWs; 00965 CurrentProcess = NULL; 00966 00967 /* Acquire the working set lock */ 00968 KeRaiseIrql(APC_LEVEL, &LockIrql); 00969 MiLockWorkingSet(CurrentThread, WorkingSet); 00970 00971 /* Re-read PTE now that we own the lock */ 00972 TempPte = *PointerPte; 00973 if (TempPte.u.Hard.Valid == 1) 00974 { 00975 // Only two things can go wrong here: 00976 // Executing NX page (we couldn't care less) 00977 // Writing to a read-only page (the stuff ARM3 works with is write, 00978 // so again, moot point). 00979 ASSERT(TempPte.u.Hard.Write == 1); 00980 00981 /* Release the working set */ 00982 MiUnlockWorkingSet(CurrentThread, WorkingSet); 00983 KeLowerIrql(LockIrql); 00984 00985 // Otherwise, the PDE was probably invalid, and all is good now 00986 return STATUS_SUCCESS; 00987 } 00988 00989 /* Check one kind of prototype PTE */ 00990 if (TempPte.u.Soft.Prototype) 00991 { 00992 /* Make sure protected pool is on, and that this is a pool address */ 00993 if ((MmProtectFreedNonPagedPool) && 00994 (((Address >= MmNonPagedPoolStart) && 00995 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart + 00996 MmSizeOfNonPagedPoolInBytes))) || 00997 ((Address >= MmNonPagedPoolExpansionStart) && 00998 (Address < MmNonPagedPoolEnd)))) 00999 { 01000 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ 01001 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL, 01002 (ULONG_PTR)Address, 01003 StoreInstruction, 01004 Mode, 01005 4); 01006 } 01007 01008 /* Get the prototype PTE! */ 01009 ProtoPte = MiProtoPteToPte(&TempPte); 01010 } 01011 else 01012 { 01013 /* We don't implement transition PTEs */ 01014 ASSERT(TempPte.u.Soft.Transition == 0); 01015 01016 /* Check for no-access PTE */ 01017 if (TempPte.u.Soft.Protection == MM_NOACCESS) 01018 { 01019 /* Bugcheck the system! */ 01020 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, 01021 (ULONG_PTR)Address, 01022 StoreInstruction, 01023 (ULONG_PTR)TrapInformation, 01024 1); 01025 } 01026 01027 /* Check for demand page */ 01028 if ((StoreInstruction) && !(TempPte.u.Hard.Valid)) 01029 { 01030 /* Get the protection code */ 01031 if (!(TempPte.u.Soft.Protection & MM_READWRITE)) 01032 { 01033 /* Bugcheck the system! */ 01034 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY, 01035 (ULONG_PTR)Address, 01036 TempPte.u.Long, 01037 (ULONG_PTR)TrapInformation, 01038 14); 01039 } 01040 } 01041 } 01042 01043 /* Now do the real fault handling */ 01044 Status = MiDispatchFault(StoreInstruction, 01045 Address, 01046 PointerPte, 01047 ProtoPte, 01048 FALSE, 01049 CurrentProcess, 01050 TrapInformation, 01051 NULL); 01052 01053 /* Release the working set */ 01054 ASSERT(KeAreAllApcsDisabled() == TRUE); 01055 MiUnlockWorkingSet(CurrentThread, WorkingSet); 01056 KeLowerIrql(LockIrql); 01057 01058 /* We are done! */ 01059 DPRINT("Fault resolved with status: %lx\n", Status); 01060 return Status; 01061 } 01062 01063 /* This is a user fault */ 01064 UserFault: 01065 CurrentThread = PsGetCurrentThread(); 01066 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process; 01067 01068 /* Lock the working set */ 01069 MiLockProcessWorkingSet(CurrentProcess, CurrentThread); 01070 01071 #if (_MI_PAGING_LEVELS == 2) 01072 ASSERT(PointerPde->u.Hard.LargePage == 0); 01073 #endif 01074 01075 #if (_MI_PAGING_LEVELS == 4) 01076 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte 01077 // also this is missing the page count increment 01078 /* Check if the PXE is valid */ 01079 if (PointerPxe->u.Hard.Valid == 0) 01080 { 01081 /* Right now, we only handle scenarios where the PXE is totally empty */ 01082 ASSERT(PointerPxe->u.Long == 0); 01083 #if 0 01084 /* Resolve a demand zero fault */ 01085 Status = MiResolveDemandZeroFault(PointerPpe, 01086 MM_READWRITE, 01087 CurrentProcess, 01088 MM_NOIRQL); 01089 #endif 01090 /* We should come back with a valid PXE */ 01091 ASSERT(PointerPxe->u.Hard.Valid == 1); 01092 } 01093 #endif 01094 01095 #if (_MI_PAGING_LEVELS >= 3) 01096 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte 01097 // also this is missing the page count increment 01098 /* Check if the PPE is valid */ 01099 if (PointerPpe->u.Hard.Valid == 0) 01100 { 01101 /* Right now, we only handle scenarios where the PPE is totally empty */ 01102 ASSERT(PointerPpe->u.Long == 0); 01103 #if 0 01104 /* Resolve a demand zero fault */ 01105 Status = MiResolveDemandZeroFault(PointerPde, 01106 MM_READWRITE, 01107 CurrentProcess, 01108 MM_NOIRQL); 01109 #endif 01110 /* We should come back with a valid PPE */ 01111 ASSERT(PointerPpe->u.Hard.Valid == 1); 01112 } 01113 #endif 01114 01115 /* Check if the PDE is valid */ 01116 if (PointerPde->u.Hard.Valid == 0) 01117 { 01118 /* Right now, we only handle scenarios where the PDE is totally empty */ 01119 ASSERT(PointerPde->u.Long == 0); 01120 01121 /* And go dispatch the fault on the PDE. This should handle the demand-zero */ 01122 #if MI_TRACE_PFNS 01123 UserPdeFault = TRUE; 01124 #endif 01125 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 01126 if (ProtectionCode == MM_NOACCESS) 01127 { 01128 #if (_MI_PAGING_LEVELS == 2) 01129 /* Could be a page table for paged pool */ 01130 MiCheckPdeForPagedPool(Address); 01131 #endif 01132 /* Has the code above changed anything -- is this now a valid PTE? */ 01133 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 01134 01135 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 01136 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 01137 return Status; 01138 } 01139 01140 /* Write a demand-zero PDE */ 01141 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde); 01142 01143 /* Dispatch the fault */ 01144 Status = MiDispatchFault(TRUE, 01145 PointerPte, 01146 PointerPde, 01147 NULL, 01148 FALSE, 01149 PsGetCurrentProcess(), 01150 TrapInformation, 01151 NULL); 01152 #if MI_TRACE_PFNS 01153 UserPdeFault = FALSE; 01154 #endif 01155 /* We should come back with APCs enabled, and with a valid PDE */ 01156 ASSERT(KeAreAllApcsDisabled() == TRUE); 01157 ASSERT(PointerPde->u.Hard.Valid == 1); 01158 } 01159 01160 /* Now capture the PTE. Ignore virtual faults for now */ 01161 TempPte = *PointerPte; 01162 ASSERT(TempPte.u.Hard.Valid == 0); 01163 01164 /* Quick check for demand-zero */ 01165 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) 01166 { 01167 /* Resolve the fault */ 01168 MiResolveDemandZeroFault(Address, 01169 PointerPte, 01170 CurrentProcess, 01171 MM_NOIRQL); 01172 01173 /* Return the status */ 01174 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 01175 return STATUS_PAGE_FAULT_DEMAND_ZERO; 01176 } 01177 01178 /* Make sure it's not a prototype PTE */ 01179 ASSERT(TempPte.u.Soft.Prototype == 0); 01180 01181 /* Check if this address range belongs to a valid allocation (VAD) */ 01182 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); 01183 if (ProtectionCode == MM_NOACCESS) 01184 { 01185 #if (_MI_PAGING_LEVELS == 2) 01186 /* Could be a page table for paged pool */ 01187 MiCheckPdeForPagedPool(Address); 01188 #endif 01189 /* Has the code above changed anything -- is this now a valid PTE? */ 01190 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION; 01191 01192 /* Either this was a bogus VA or we've fixed up a paged pool PDE */ 01193 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 01194 return Status; 01195 } 01196 01197 /* Check for non-demand zero PTE */ 01198 if (TempPte.u.Long != 0) 01199 { 01200 /* This is a page fault */ 01201 01202 /* FIXME: Run MiAccessCheck */ 01203 01204 /* Dispatch the fault */ 01205 Status = MiDispatchFault(StoreInstruction, 01206 Address, 01207 PointerPte, 01208 NULL, 01209 FALSE, 01210 PsGetCurrentProcess(), 01211 TrapInformation, 01212 NULL); 01213 01214 /* Return the status */ 01215 ASSERT(NT_SUCCESS(Status)); 01216 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 01217 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 01218 return Status; 01219 } 01220 01221 /* 01222 * Check if this is a real user-mode address or actually a kernel-mode 01223 * page table for a user mode address 01224 */ 01225 if (Address <= MM_HIGHEST_USER_ADDRESS) 01226 { 01227 /* Add an additional page table reference */ 01228 MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++; 01229 ASSERT(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT); 01230 } 01231 01232 /* No guard page support yet */ 01233 ASSERT((ProtectionCode & MM_DECOMMIT) == 0); 01234 01235 /* Did we get a prototype PTE back? */ 01236 if (!ProtoPte) 01237 { 01238 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */ 01239 if (PointerPde == MiAddressToPde(PTE_BASE)) 01240 { 01241 /* Then it's really a demand-zero PDE (on behalf of user-mode) */ 01242 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde); 01243 } 01244 else 01245 { 01246 /* No, create a new PTE. First, write the protection */ 01247 PointerPte->u.Soft.Protection = ProtectionCode; 01248 } 01249 01250 /* Lock the PFN database since we're going to grab a page */ 01251 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 01252 01253 /* Try to get a zero page */ 01254 MI_SET_USAGE(MI_USAGE_PEB_TEB); 01255 MI_SET_PROCESS2(CurrentProcess->ImageFileName); 01256 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); 01257 PageFrameIndex = MiRemoveZeroPageSafe(Color); 01258 if (!PageFrameIndex) 01259 { 01260 /* Grab a page out of there. Later we should grab a colored zero page */ 01261 PageFrameIndex = MiRemoveAnyPage(Color); 01262 ASSERT(PageFrameIndex); 01263 01264 /* Release the lock since we need to do some zeroing */ 01265 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 01266 01267 /* Zero out the page, since it's for user-mode */ 01268 MiZeroPfn(PageFrameIndex); 01269 01270 /* Grab the lock again so we can initialize the PFN entry */ 01271 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 01272 } 01273 01274 /* Initialize the PFN entry now */ 01275 MiInitializePfn(PageFrameIndex, PointerPte, 1); 01276 01277 /* One more demand-zero fault */ 01278 KeGetCurrentPrcb()->MmDemandZeroCount++; 01279 01280 /* And we're done with the lock */ 01281 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 01282 01283 /* Fault on user PDE, or fault on user PTE? */ 01284 if (PointerPte <= MiHighestUserPte) 01285 { 01286 /* User fault, build a user PTE */ 01287 MI_MAKE_HARDWARE_PTE_USER(&TempPte, 01288 PointerPte, 01289 PointerPte->u.Soft.Protection, 01290 PageFrameIndex); 01291 } 01292 else 01293 { 01294 /* This is a user-mode PDE, create a kernel PTE for it */ 01295 MI_MAKE_HARDWARE_PTE(&TempPte, 01296 PointerPte, 01297 PointerPte->u.Soft.Protection, 01298 PageFrameIndex); 01299 } 01300 01301 /* Write the dirty bit for writeable pages */ 01302 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte); 01303 01304 /* And now write down the PTE, making the address valid */ 01305 MI_WRITE_VALID_PTE(PointerPte, TempPte); 01306 ASSERT(MiGetPfnEntry(PageFrameIndex)->u1.Event == NULL); 01307 01308 /* Demand zero */ 01309 Status = STATUS_PAGE_FAULT_DEMAND_ZERO; 01310 } 01311 else 01312 { 01313 /* No guard page support yet */ 01314 ASSERT((ProtectionCode & MM_DECOMMIT) == 0); 01315 ASSERT(ProtectionCode != 0x100); 01316 01317 /* Write the prototype PTE */ 01318 TempPte = PrototypePte; 01319 TempPte.u.Soft.Protection = ProtectionCode; 01320 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 01321 01322 /* Handle the fault */ 01323 Status = MiDispatchFault(StoreInstruction, 01324 Address, 01325 PointerPte, 01326 ProtoPte, 01327 FALSE, 01328 CurrentProcess, 01329 TrapInformation, 01330 Vad); 01331 ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION); 01332 ASSERT(PointerPte->u.Hard.Valid == 1); 01333 ASSERT(PointerPte->u.Hard.PageFrameNumber != 0); 01334 } 01335 01336 /* Release the working set */ 01337 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 01338 return Status; 01339 } 01340 01341 NTSTATUS 01342 NTAPI 01343 MmGetExecuteOptions(IN PULONG ExecuteOptions) 01344 { 01345 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 01346 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 01347 01348 *ExecuteOptions = 0; 01349 01350 if (CurrentProcess->Flags.ExecuteDisable) 01351 { 01352 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE; 01353 } 01354 01355 if (CurrentProcess->Flags.ExecuteEnable) 01356 { 01357 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE; 01358 } 01359 01360 if (CurrentProcess->Flags.DisableThunkEmulation) 01361 { 01362 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION; 01363 } 01364 01365 if (CurrentProcess->Flags.Permanent) 01366 { 01367 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT; 01368 } 01369 01370 if (CurrentProcess->Flags.ExecuteDispatchEnable) 01371 { 01372 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE; 01373 } 01374 01375 if (CurrentProcess->Flags.ImageDispatchEnable) 01376 { 01377 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE; 01378 } 01379 01380 return STATUS_SUCCESS; 01381 } 01382 01383 NTSTATUS 01384 NTAPI 01385 MmSetExecuteOptions(IN ULONG ExecuteOptions) 01386 { 01387 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb; 01388 KLOCK_QUEUE_HANDLE ProcessLock; 01389 NTSTATUS Status = STATUS_ACCESS_DENIED; 01390 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 01391 01392 /* Only accept valid flags */ 01393 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS) 01394 { 01395 /* Fail */ 01396 DPRINT1("Invalid no-execute options\n"); 01397 return STATUS_INVALID_PARAMETER; 01398 } 01399 01400 /* Change the NX state in the process lock */ 01401 KiAcquireProcessLock(CurrentProcess, &ProcessLock); 01402 01403 /* Don't change anything if the permanent flag was set */ 01404 if (!CurrentProcess->Flags.Permanent) 01405 { 01406 /* Start by assuming it's not disabled */ 01407 CurrentProcess->Flags.ExecuteDisable = FALSE; 01408 01409 /* Now process each flag and turn the equivalent bit on */ 01410 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE) 01411 { 01412 CurrentProcess->Flags.ExecuteDisable = TRUE; 01413 } 01414 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE) 01415 { 01416 CurrentProcess->Flags.ExecuteEnable = TRUE; 01417 } 01418 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) 01419 { 01420 CurrentProcess->Flags.DisableThunkEmulation = TRUE; 01421 } 01422 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT) 01423 { 01424 CurrentProcess->Flags.Permanent = TRUE; 01425 } 01426 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE) 01427 { 01428 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 01429 } 01430 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE) 01431 { 01432 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 01433 } 01434 01435 /* These are turned on by default if no-execution is also eanbled */ 01436 if (CurrentProcess->Flags.ExecuteEnable) 01437 { 01438 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE; 01439 CurrentProcess->Flags.ImageDispatchEnable = TRUE; 01440 } 01441 01442 /* All good */ 01443 Status = STATUS_SUCCESS; 01444 } 01445 01446 /* Release the lock and return status */ 01447 KiReleaseProcessLock(&ProcessLock); 01448 return Status; 01449 } 01450 01451 /* EOF */ Generated on Sat May 26 2012 04:36:22 for ReactOS by
1.7.6.1
|