2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #line 15 "ARMĀ³::PAGFAULT"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 /* GLOBALS ********************************************************************/
21 /* PRIVATE FUNCTIONS **********************************************************/
25 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
26 OUT PULONG ProtectCode
,
32 /* No prototype/section support for now */
35 /* Check if this is a page table address */
36 if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
38 /* This should never happen, as these addresses are handled by the double-maping */
39 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
40 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
42 /* Fail such access */
43 *ProtectCode
= MM_NOACCESS
;
47 /* Return full access rights */
48 *ProtectCode
= MM_READWRITE
;
52 /* Should not be a session address */
53 ASSERT(MI_IS_SESSION_ADDRESS(VirtualAddress
) == FALSE
);
55 /* Special case for shared data */
56 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)USER_SHARED_DATA
)
58 /* It's a read-only page */
59 *ProtectCode
= MM_READONLY
;
60 return MmSharedUserDataPte
;
63 /* Find the VAD, it might not exist if the address is bogus */
64 Vad
= MiLocateAddress(VirtualAddress
);
67 /* Bogus virtual address */
68 *ProtectCode
= MM_NOACCESS
;
72 /* This must be a VM VAD */
73 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
75 /* Check if it's a section, or just an allocation */
76 if (Vad
->u
.VadFlags
.PrivateMemory
== TRUE
)
78 /* This must be a TEB/PEB VAD */
79 ASSERT(Vad
->u
.VadFlags
.MemCommit
== TRUE
);
80 *ProtectCode
= Vad
->u
.VadFlags
.Protection
;
85 /* Return the proto VAD */
86 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
89 /* Get the prototype PTE for this page */
90 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
91 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
92 ASSERT(PointerPte
!= NULL
);
94 /* Return the Prototype PTE and the protection for the page mapping */
95 *ProtectCode
= Vad
->u
.VadFlags
.Protection
;
102 MiCheckPdeForPagedPool(IN PVOID Address
)
105 NTSTATUS Status
= STATUS_SUCCESS
;
107 /* No session support in ReactOS yet */
108 ASSERT(MI_IS_SESSION_ADDRESS(Address
) == FALSE
);
109 ASSERT(MI_IS_SESSION_PTE(Address
) == FALSE
);
112 // Check if this is a fault while trying to access the page table itself
114 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
117 // Send a hint to the page fault handler that this is only a valid fault
118 // if we already detected this was access within the page table range
120 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
121 Status
= STATUS_WAIT_1
;
123 else if (Address
< MmSystemRangeStart
)
126 // This is totally illegal
128 return STATUS_ACCESS_VIOLATION
;
133 // Get the PDE for the address
135 PointerPde
= MiAddressToPde(Address
);
139 // Check if it's not valid
141 if (PointerPde
->u
.Hard
.Valid
== 0)
147 // Copy it from our double-mapped system page directory
149 InterlockedExchangePte(PointerPde
,
150 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
162 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
169 /* Get the PFN for this page */
170 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
173 /* Grab a system PTE we can use to zero the page */
174 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
177 /* Initialize the PTE for it */
178 TempPte
= ValidKernelPte
;
179 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
182 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
184 /* Write combining, no caching */
185 MI_PAGE_DISABLE_CACHE(&TempPte
);
186 MI_PAGE_WRITE_COMBINED(&TempPte
);
188 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
190 /* Write through, no caching */
191 MI_PAGE_DISABLE_CACHE(&TempPte
);
192 MI_PAGE_WRITE_THROUGH(&TempPte
);
195 /* Make the system PTE valid with our PFN */
196 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
198 /* Get the address it maps to, and zero it out */
199 ZeroAddress
= MiPteToAddress(ZeroPte
);
200 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
202 /* Now get rid of it */
203 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
208 MiResolveDemandZeroFault(IN PVOID Address
,
209 IN PMMPTE PointerPte
,
210 IN PEPROCESS Process
,
213 PFN_NUMBER PageFrameNumber
= 0;
215 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
217 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
221 /* Must currently only be called by paging path */
222 if ((Process
) && (OldIrql
== MM_NOIRQL
))
225 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
228 ASSERT(Process
->ForkInProgress
== NULL
);
230 /* Get process color */
231 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
232 ASSERT(Color
!= 0xFFFFFFFF);
234 /* We'll need a zero page */
239 /* Check if we need a zero page */
240 NeedZero
= (OldIrql
!= MM_NOIRQL
);
242 /* Get the next system page color */
243 Color
= MI_GET_NEXT_COLOR();
246 /* Check if the PFN database should be acquired */
247 if (OldIrql
== MM_NOIRQL
)
249 /* Acquire it and remember we should release it after */
250 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
254 /* We either manually locked the PFN DB, or already came with it locked */
255 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
257 /* Do we need a zero page? */
258 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
259 if ((NeedZero
) && (Process
))
261 /* Try to get one, if we couldn't grab a free page and zero it */
262 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
265 /* We got a genuine zero page, stop worrying about it */
270 /* We'll need a free page and zero it manually */
271 PageFrameNumber
= MiRemoveAnyPage(Color
);
276 /* Process or system doesn't want a zero page, grab anything */
277 PageFrameNumber
= MiRemoveAnyPage(Color
);
281 /* System wants a zero page, obtain one */
282 PageFrameNumber
= MiRemoveZeroPage(Color
);
286 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
288 /* Release PFN lock if needed */
289 if (HaveLock
) KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
292 // Increment demand zero faults
294 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
296 /* Zero the page if need be */
297 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
300 if (PointerPte
<= MiHighestUserPte
)
303 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
305 PointerPte
->u
.Soft
.Protection
,
310 /* For kernel mode */
311 MI_MAKE_HARDWARE_PTE(&TempPte
,
313 PointerPte
->u
.Soft
.Protection
,
317 /* Set it dirty if it's a writable page */
318 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
321 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
326 DPRINT("Paged pool page has now been paged in\n");
327 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
332 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
334 IN PMMPTE PointerPte
,
335 IN PMMPTE PointerProtoPte
,
342 PFN_NUMBER PageFrameIndex
;
344 /* Must be called with an valid prototype PTE, with the PFN lock held */
345 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
346 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
349 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
351 /* Get the PFN entry and set it as a prototype PTE */
352 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
353 Pfn1
->u3
.e1
.PrototypePte
= 1;
355 /* FIXME: Increment the share count for the page table */
357 /* Check where we should be getting the protection information from */
358 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
360 /* Get the protection from the PTE, there's no real Proto PTE data */
361 Protection
= PointerPte
->u
.Soft
.Protection
;
365 /* Get the protection from the original PTE link */
366 OriginalPte
= &Pfn1
->OriginalPte
;
367 Protection
= OriginalPte
->u
.Soft
.Protection
;
370 /* Release the PFN lock */
371 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
373 /* Remove caching bits */
374 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
376 /* Check if this is a kernel or user address */
377 if (Address
< MmSystemRangeStart
)
379 /* Build the user PTE */
380 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
384 /* Build the kernel PTE */
385 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
389 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
392 return STATUS_SUCCESS
;
397 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
399 IN PMMPTE PointerPte
,
400 IN PMMPTE PointerProtoPte
,
401 IN OUT PMMPFN
*OutPfn
,
402 OUT PVOID
*PageFileData
,
404 IN PEPROCESS Process
,
406 IN PVOID TrapInformation
)
410 PFN_NUMBER PageFrameIndex
;
413 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
414 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
415 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
416 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
418 /* Read the prototype PTE and check if it's valid */
419 TempPte
= *PointerProtoPte
;
420 if (TempPte
.u
.Hard
.Valid
== 1)
422 /* One more user of this mapped page */
423 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
424 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
425 Pfn1
->u2
.ShareCount
++;
427 /* Call it a transition */
428 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
430 /* Complete the prototype PTE fault -- this will release the PFN lock */
431 return MiCompleteProtoPteFault(StoreInstruction
,
439 /* Make sure there's some protection mask */
440 if (TempPte
.u
.Long
== 0)
442 /* Release the lock */
443 DPRINT1("Access on reserved section?\n");
444 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
445 return STATUS_ACCESS_VIOLATION
;
448 /* This is the only thing we support right now */
449 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
450 ASSERT(TempPte
.u
.Proto
.ReadOnly
== 0);
451 ASSERT(PointerPte
> MiHighestUserPte
);
452 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
453 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
455 /* Resolve the demand zero fault */
456 Status
= MiResolveDemandZeroFault(Address
, PointerProtoPte
, Process
, OldIrql
);
457 ASSERT(NT_SUCCESS(Status
));
459 /* Complete the prototype PTE fault -- this will release the PFN lock */
460 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
461 return MiCompleteProtoPteFault(StoreInstruction
,
471 MiDispatchFault(IN BOOLEAN StoreInstruction
,
473 IN PMMPTE PointerPte
,
474 IN PMMPTE PointerProtoPte
,
475 IN BOOLEAN Recursive
,
476 IN PEPROCESS Process
,
477 IN PVOID TrapInformation
,
481 KIRQL OldIrql
, LockIrql
;
483 PMMPTE SuperProtoPte
;
484 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
489 // Make sure APCs are off and we're not at dispatch
491 OldIrql
= KeGetCurrentIrql();
492 ASSERT(OldIrql
<= APC_LEVEL
);
493 ASSERT(KeAreAllApcsDisabled() == TRUE
);
496 // Grab a copy of the PTE
498 TempPte
= *PointerPte
;
500 /* Do we have a prototype PTE? */
503 /* This should never happen */
504 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
506 /* Check if this is a kernel-mode address */
507 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
508 if (Address
>= MmSystemRangeStart
)
510 /* Lock the PFN database */
511 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
513 /* Has the PTE been made valid yet? */
514 if (!SuperProtoPte
->u
.Hard
.Valid
)
521 /* Resolve the fault -- this will release the PFN lock */
522 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
523 Status
= MiResolveProtoPteFault(StoreInstruction
,
533 ASSERT(Status
== STATUS_SUCCESS
);
535 /* Complete this as a transition fault */
536 ASSERT(OldIrql
== KeGetCurrentIrql());
537 ASSERT(OldIrql
<= APC_LEVEL
);
538 ASSERT(KeAreAllApcsDisabled() == TRUE
);
544 /* We currently only handle very limited paths */
545 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
546 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
548 /* Lock the PFN database */
549 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
551 /* For our current usage, this should be true */
552 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
553 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
555 /* Resolve the fault -- this will release the PFN lock */
556 Status
= MiResolveProtoPteFault(StoreInstruction
,
566 ASSERT(Status
== STATUS_SUCCESS
);
568 /* Complete this as a transition fault */
569 ASSERT(OldIrql
== KeGetCurrentIrql());
570 ASSERT(OldIrql
<= APC_LEVEL
);
571 ASSERT(KeAreAllApcsDisabled() == TRUE
);
572 return STATUS_PAGE_FAULT_TRANSITION
;
577 // The PTE must be invalid, but not totally blank
579 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
580 ASSERT(TempPte
.u
.Long
!= 0);
583 // No prototype, transition or page file software PTEs in ARM3 yet
585 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
586 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
587 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
590 // If we got this far, the PTE can only be a demand zero PTE, which is what
591 // we want. Go handle it!
593 Status
= MiResolveDemandZeroFault(Address
,
597 ASSERT(KeAreAllApcsDisabled () == TRUE
);
598 if (NT_SUCCESS(Status
))
601 // Make sure we're returning in a sane state and pass the status down
603 ASSERT(OldIrql
== KeGetCurrentIrql ());
604 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
609 // Generate an access fault
611 return STATUS_ACCESS_VIOLATION
;
616 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
618 IN KPROCESSOR_MODE Mode
,
619 IN PVOID TrapInformation
)
621 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
622 PMMPTE PointerPte
, ProtoPte
= NULL
;
625 PETHREAD CurrentThread
;
626 PEPROCESS CurrentProcess
;
628 PMMSUPPORT WorkingSet
;
629 ULONG ProtectionCode
;
631 PFN_NUMBER PageFrameIndex
;
633 DPRINT("ARM3 FAULT AT: %p\n", Address
);
636 // Get the PTE and PDE
638 PointerPte
= MiAddressToPte(Address
);
639 PointerPde
= MiAddressToPde(Address
);
640 #if (_MI_PAGING_LEVELS >= 3)
641 /* We need the PPE and PXE addresses */
646 // Check for dispatch-level snafu
648 if (OldIrql
> APC_LEVEL
)
651 // There are some special cases where this is okay, but not in ARM3 yet
653 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
656 ASSERT(OldIrql
<= APC_LEVEL
);
660 // Check for kernel fault
662 while (Address
>= MmSystemRangeStart
)
665 // What are you even DOING here?
667 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
669 #if (_MI_PAGING_LEVELS >= 3)
670 /* Need to check PXE and PDE validity */
677 if (!PointerPde
->u
.Hard
.Valid
== 0)
682 DPRINT("Invalid PDE\n");
683 #if (_MI_PAGING_LEVELS == 2)
685 // Handle mapping in "Special" PDE directoreis
687 MiCheckPdeForPagedPool(Address
);
690 // Now we SHOULD be good
692 if (PointerPde
->u
.Hard
.Valid
== 0)
695 // FIXFIX: Do the S-LIST hack
701 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
704 (ULONG_PTR
)TrapInformation
,
710 // The PDE is valid, so read the PTE
712 TempPte
= *PointerPte
;
713 if (TempPte
.u
.Hard
.Valid
== 1)
716 // Only two things can go wrong here:
717 // Executing NX page (we couldn't care less)
718 // Writing to a read-only page (the stuff ARM3 works with is write,
719 // so again, moot point).
721 if (StoreInstruction
)
723 DPRINT1("Should NEVER happen on ARM3!!!\n");
724 return STATUS_ACCESS_VIOLATION
;
728 // Otherwise, the PDE was probably invalid, and all is good now
730 return STATUS_SUCCESS
;
734 // Check for a fault on the page table or hyperspace itself
736 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
738 #if (_MI_PAGING_LEVELS == 2)
739 /* Could be paged pool access from a new process -- synchronize the page directories */
740 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
742 DPRINT1("PAGE TABLES FAULTED IN!\n");
743 return STATUS_SUCCESS
;
746 /* Otherwise this could be a commit of a virtual address */
750 /* In this path, we are using the system working set */
751 CurrentThread
= PsGetCurrentThread();
752 WorkingSet
= &MmSystemCacheWs
;
755 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
756 MiLockWorkingSet(CurrentThread
, WorkingSet
);
759 // Re-read PTE now that the IRQL has been raised
761 TempPte
= *PointerPte
;
762 if (TempPte
.u
.Hard
.Valid
== 1)
765 // Only two things can go wrong here:
766 // Executing NX page (we couldn't care less)
767 // Writing to a read-only page (the stuff ARM3 works with is write,
768 // so again, moot point.
770 if (StoreInstruction
)
772 DPRINT1("Should NEVER happen on ARM3!!!\n");
773 return STATUS_ACCESS_VIOLATION
;
776 /* Release the working set */
777 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
778 KeLowerIrql(LockIrql
);
781 // Otherwise, the PDE was probably invalid, and all is good now
783 return STATUS_SUCCESS
;
786 /* Check one kind of prototype PTE */
787 if (TempPte
.u
.Soft
.Prototype
)
789 /* Make sure protected pool is on, and that this is a pool address */
790 if ((MmProtectFreedNonPagedPool
) &&
791 (((Address
>= MmNonPagedPoolStart
) &&
792 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
793 MmSizeOfNonPagedPoolInBytes
))) ||
794 ((Address
>= MmNonPagedPoolExpansionStart
) &&
795 (Address
< MmNonPagedPoolEnd
))))
797 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
798 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
805 /* Get the prototype PTE! */
806 ProtoPte
= MiProtoPteToPte(&TempPte
);
811 // We don't implement transition PTEs
813 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
815 /* Check for no-access PTE */
816 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
818 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
819 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
822 (ULONG_PTR
)TrapInformation
,
827 /* Check for demand page */
828 if ((StoreInstruction
) && !(ProtoPte
) && !(TempPte
.u
.Hard
.Valid
))
830 /* Get the protection code */
831 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
833 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
834 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
837 (ULONG_PTR
)TrapInformation
,
843 // Now do the real fault handling
845 Status
= MiDispatchFault(StoreInstruction
,
854 /* Release the working set */
855 ASSERT(KeAreAllApcsDisabled() == TRUE
);
856 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
857 KeLowerIrql(LockIrql
);
862 DPRINT("Fault resolved with status: %lx\n", Status
);
866 /* This is a user fault */
867 CurrentThread
= PsGetCurrentThread();
868 CurrentProcess
= PsGetCurrentProcess();
870 /* Lock the working set */
871 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
873 #if (_MI_PAGING_LEVELS >= 3)
874 /* Need to check/handle PPE and PXE validity too */
878 /* First things first, is the PDE valid? */
879 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
880 if (PointerPde
->u
.Hard
.Valid
== 0)
882 /* Right now, we only handle scenarios where the PDE is totally empty */
883 ASSERT(PointerPde
->u
.Long
== 0);
885 /* Check if this address range belongs to a valid allocation (VAD) */
886 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
888 /* Right now, we expect a valid protection mask on the VAD */
889 ASSERT(ProtectionCode
!= MM_NOACCESS
);
891 /* Make the PDE demand-zero */
892 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
894 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
895 Status
= MiDispatchFault(TRUE
,
900 PsGetCurrentProcess(),
904 /* We should come back with APCs enabled, and with a valid PDE */
905 ASSERT(KeAreAllApcsDisabled() == TRUE
);
906 #if (_MI_PAGING_LEVELS >= 3)
907 /* Need to check/handle PPE and PXE validity too */
910 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
913 /* Now capture the PTE. Ignore virtual faults for now */
914 TempPte
= *PointerPte
;
915 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
917 /* Quick check for demand-zero */
918 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
920 /* Resolve the fault */
921 MiResolveDemandZeroFault(Address
,
926 /* Return the status */
927 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
928 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
931 /* Get protection and check if it's a prototype PTE */
932 ProtectionCode
= TempPte
.u
.Soft
.Protection
;
933 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
935 /* Check for non-demand zero PTE */
936 if (TempPte
.u
.Long
!= 0)
938 /* This is a page fault, check for valid protection */
939 ASSERT(ProtectionCode
!= 0x100);
941 /* FIXME: Run MiAccessCheck */
943 /* Dispatch the fault */
944 Status
= MiDispatchFault(StoreInstruction
,
949 PsGetCurrentProcess(),
953 /* Return the status */
954 ASSERT(NT_SUCCESS(Status
));
955 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
956 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
960 /* Check if this address range belongs to a valid allocation (VAD) */
961 ASSERT(TempPte
.u
.Long
== 0);
962 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
963 if (ProtectionCode
== MM_NOACCESS
)
965 /* This is a bogus VA */
966 Status
= STATUS_ACCESS_VIOLATION
;
968 /* Could be a not-yet-mapped paged pool page table */
969 #if (_MI_PAGING_LEVELS == 2)
970 MiCheckPdeForPagedPool(Address
);
972 /* See if that fixed it */
973 if (PointerPte
->u
.Hard
.Valid
== 1) Status
= STATUS_SUCCESS
;
975 /* Return the status */
976 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
980 /* Did we get a prototype PTE back? */
983 /* No, create a new PTE. First, write the protection */
984 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
986 /* Lock the PFN database since we're going to grab a page */
987 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
989 /* Try to get a zero page */
990 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
991 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
994 /* Grab a page out of there. Later we should grab a colored zero page */
995 PageFrameIndex
= MiRemoveAnyPage(Color
);
996 ASSERT(PageFrameIndex
);
998 /* Release the lock since we need to do some zeroing */
999 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1001 /* Zero out the page, since it's for user-mode */
1002 MiZeroPfn(PageFrameIndex
);
1004 /* Grab the lock again so we can initialize the PFN entry */
1005 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1008 /* Initialize the PFN entry now */
1009 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1011 /* And we're done with the lock */
1012 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1014 /* One more demand-zero fault */
1015 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount
);
1017 /* Was the fault on an actual user page, or a kernel page for the user? */
1018 if (PointerPte
<= MiHighestUserPte
)
1020 /* User fault, build a user PTE */
1021 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1023 PointerPte
->u
.Soft
.Protection
,
1028 /* Session, kernel, or user PTE, figure it out and build it */
1029 MI_MAKE_HARDWARE_PTE(&TempPte
,
1031 PointerPte
->u
.Soft
.Protection
,
1035 /* Write the dirty bit for writeable pages */
1036 if (TempPte
.u
.Hard
.Write
) TempPte
.u
.Hard
.Dirty
= TRUE
;
1038 /* And now write down the PTE, making the address valid */
1039 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1042 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
1046 /* No guard page support yet */
1047 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1048 ASSERT(ProtectionCode
!= 0x100);
1050 /* Write the prototype PTE */
1051 TempPte
= PrototypePte
;
1052 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1053 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1055 /* Handle the fault */
1056 Status
= MiDispatchFault(StoreInstruction
,
1064 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
1065 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1066 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
!= 0);
1069 /* Release the working set */
1070 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);