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 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
18 /* GLOBALS ********************************************************************/
21 BOOLEAN UserPdeFault
= FALSE
;
24 /* PRIVATE FUNCTIONS **********************************************************/
28 MiCheckVirtualAddress(IN PVOID VirtualAddress
,
29 OUT PULONG ProtectCode
,
35 /* No prototype/section support for now */
38 /* Check if this is a page table address */
39 if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress
))
41 /* This should never happen, as these addresses are handled by the double-maping */
42 if (((PMMPTE
)VirtualAddress
>= MiAddressToPte(MmPagedPoolStart
)) &&
43 ((PMMPTE
)VirtualAddress
<= MmPagedPoolInfo
.LastPteForPagedPool
))
45 /* Fail such access */
46 *ProtectCode
= MM_NOACCESS
;
50 /* Return full access rights */
51 *ProtectCode
= MM_READWRITE
;
55 /* Should not be a session address */
56 ASSERT(MI_IS_SESSION_ADDRESS(VirtualAddress
) == FALSE
);
58 /* Special case for shared data */
59 if (PAGE_ALIGN(VirtualAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
)
61 /* It's a read-only page */
62 *ProtectCode
= MM_READONLY
;
63 return MmSharedUserDataPte
;
66 /* Find the VAD, it might not exist if the address is bogus */
67 Vad
= MiLocateAddress(VirtualAddress
);
70 /* Bogus virtual address */
71 *ProtectCode
= MM_NOACCESS
;
75 /* This must be a VM VAD */
76 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
78 /* Check if it's a section, or just an allocation */
79 if (Vad
->u
.VadFlags
.PrivateMemory
== TRUE
)
81 /* This must be a TEB/PEB VAD */
82 ASSERT(Vad
->u
.VadFlags
.MemCommit
== TRUE
);
83 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
88 /* Return the proto VAD */
89 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
92 /* Get the prototype PTE for this page */
93 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
94 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
95 ASSERT(PointerPte
!= NULL
);
97 /* Return the Prototype PTE and the protection for the page mapping */
98 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
103 #if (_MI_PAGING_LEVELS == 2)
106 MiSynchronizeSystemPde(PMMPDE PointerPde
)
111 /* Get the Index from the PDE */
112 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
114 /* Copy the PDE from the double-mapped system page directory */
115 SystemPde
= MmSystemPagePtes
[Index
];
116 *PointerPde
= SystemPde
;
118 /* Make sure we re-read the PDE and PTE */
119 KeMemoryBarrierWithoutFence();
121 /* Return, if we had success */
122 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
127 MiCheckPdeForPagedPool(IN PVOID Address
)
130 NTSTATUS Status
= STATUS_SUCCESS
;
132 /* No session support in ReactOS yet */
133 ASSERT(MI_IS_SESSION_ADDRESS(Address
) == FALSE
);
134 ASSERT(MI_IS_SESSION_PTE(Address
) == FALSE
);
137 // Check if this is a fault while trying to access the page table itself
139 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
142 // Send a hint to the page fault handler that this is only a valid fault
143 // if we already detected this was access within the page table range
145 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
146 Status
= STATUS_WAIT_1
;
148 else if (Address
< MmSystemRangeStart
)
151 // This is totally illegal
153 return STATUS_ACCESS_VIOLATION
;
158 // Get the PDE for the address
160 PointerPde
= MiAddressToPde(Address
);
164 // Check if it's not valid
166 if (PointerPde
->u
.Hard
.Valid
== 0)
172 // Copy it from our double-mapped system page directory
174 InterlockedExchangePte(PointerPde
,
175 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
188 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
195 /* Get the PFN for this page */
196 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
199 /* Grab a system PTE we can use to zero the page */
200 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
203 /* Initialize the PTE for it */
204 TempPte
= ValidKernelPte
;
205 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
208 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
210 /* Write combining, no caching */
211 MI_PAGE_DISABLE_CACHE(&TempPte
);
212 MI_PAGE_WRITE_COMBINED(&TempPte
);
214 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
216 /* Write through, no caching */
217 MI_PAGE_DISABLE_CACHE(&TempPte
);
218 MI_PAGE_WRITE_THROUGH(&TempPte
);
221 /* Make the system PTE valid with our PFN */
222 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
224 /* Get the address it maps to, and zero it out */
225 ZeroAddress
= MiPteToAddress(ZeroPte
);
226 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
228 /* Now get rid of it */
229 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
234 MiResolveDemandZeroFault(IN PVOID Address
,
236 IN PEPROCESS Process
,
239 PFN_NUMBER PageFrameNumber
= 0;
240 PMMPTE PointerPte
= MiAddressToPte(Address
);
242 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
244 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
248 /* Must currently only be called by paging path */
249 if ((Process
) && (OldIrql
== MM_NOIRQL
))
252 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
255 ASSERT(Process
->ForkInProgress
== NULL
);
257 /* Get process color */
258 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
259 ASSERT(Color
!= 0xFFFFFFFF);
261 /* We'll need a zero page */
266 /* Check if we need a zero page */
267 NeedZero
= (OldIrql
!= MM_NOIRQL
);
269 /* Get the next system page color */
270 Color
= MI_GET_NEXT_COLOR();
273 /* Check if the PFN database should be acquired */
274 if (OldIrql
== MM_NOIRQL
)
276 /* Acquire it and remember we should release it after */
277 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
281 /* We either manually locked the PFN DB, or already came with it locked */
282 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
284 /* Do we need a zero page? */
285 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
287 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
288 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
290 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
291 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
292 if ((NeedZero
) && (Process
))
294 /* Try to get one, if we couldn't grab a free page and zero it */
295 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
298 /* We got a genuine zero page, stop worrying about it */
303 /* We'll need a free page and zero it manually */
304 PageFrameNumber
= MiRemoveAnyPage(Color
);
309 /* Process or system doesn't want a zero page, grab anything */
310 PageFrameNumber
= MiRemoveAnyPage(Color
);
314 /* System wants a zero page, obtain one */
315 PageFrameNumber
= MiRemoveZeroPage(Color
);
319 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
321 /* Increment demand zero faults */
322 KeGetCurrentPrcb()->MmDemandZeroCount
++;
324 /* Release PFN lock if needed */
325 if (HaveLock
) KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
327 /* Zero the page if need be */
328 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
331 if (PointerPte
<= MiHighestUserPte
)
334 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
341 /* For kernel mode */
342 MI_MAKE_HARDWARE_PTE(&TempPte
,
348 /* Set it dirty if it's a writable page */
349 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
352 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
357 DPRINT("Paged pool page has now been paged in\n");
358 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
363 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
365 IN PMMPTE PointerPte
,
366 IN PMMPTE PointerProtoPte
,
372 ULONG_PTR Protection
;
373 PFN_NUMBER PageFrameIndex
;
375 /* Must be called with an valid prototype PTE, with the PFN lock held */
376 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
377 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
380 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
382 /* Get the PFN entry and set it as a prototype PTE */
383 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
384 Pfn1
->u3
.e1
.PrototypePte
= 1;
386 /* FIXME: Increment the share count for the page table */
388 /* Check where we should be getting the protection information from */
389 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
391 /* Get the protection from the PTE, there's no real Proto PTE data */
392 Protection
= PointerPte
->u
.Soft
.Protection
;
396 /* Get the protection from the original PTE link */
397 OriginalPte
= &Pfn1
->OriginalPte
;
398 Protection
= OriginalPte
->u
.Soft
.Protection
;
401 /* Release the PFN lock */
402 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
404 /* Remove caching bits */
405 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
407 /* Check if this is a kernel or user address */
408 if (Address
< MmSystemRangeStart
)
410 /* Build the user PTE */
411 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
415 /* Build the kernel PTE */
416 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
420 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
423 return STATUS_SUCCESS
;
428 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
430 IN PMMPTE PointerPte
,
431 IN PMMPTE PointerProtoPte
,
432 IN OUT PMMPFN
*OutPfn
,
433 OUT PVOID
*PageFileData
,
435 IN PEPROCESS Process
,
437 IN PVOID TrapInformation
)
441 PFN_NUMBER PageFrameIndex
;
444 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
445 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
446 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
447 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
449 /* Read the prototype PTE and check if it's valid */
450 TempPte
= *PointerProtoPte
;
451 if (TempPte
.u
.Hard
.Valid
== 1)
453 /* One more user of this mapped page */
454 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
455 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
456 Pfn1
->u2
.ShareCount
++;
458 /* Call it a transition */
459 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
461 /* Complete the prototype PTE fault -- this will release the PFN lock */
462 return MiCompleteProtoPteFault(StoreInstruction
,
470 /* Make sure there's some protection mask */
471 if (TempPte
.u
.Long
== 0)
473 /* Release the lock */
474 DPRINT1("Access on reserved section?\n");
475 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
476 return STATUS_ACCESS_VIOLATION
;
479 /* This is the only thing we support right now */
480 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
481 ASSERT(TempPte
.u
.Proto
.ReadOnly
== 0);
482 ASSERT(PointerPte
> MiHighestUserPte
);
483 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
484 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
486 /* Resolve the demand zero fault */
487 Status
= MiResolveDemandZeroFault(Address
,
488 (ULONG
)PointerProtoPte
->u
.Soft
.Protection
,
491 ASSERT(NT_SUCCESS(Status
));
493 /* Complete the prototype PTE fault -- this will release the PFN lock */
494 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
495 return MiCompleteProtoPteFault(StoreInstruction
,
505 MiDispatchFault(IN BOOLEAN StoreInstruction
,
507 IN PMMPTE PointerPte
,
508 IN PMMPTE PointerProtoPte
,
509 IN BOOLEAN Recursive
,
510 IN PEPROCESS Process
,
511 IN PVOID TrapInformation
,
515 KIRQL OldIrql
, LockIrql
;
517 PMMPTE SuperProtoPte
;
518 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
522 /* Make sure the addresses are ok */
523 ASSERT(PointerPte
== MiAddressToPte(Address
));
526 // Make sure APCs are off and we're not at dispatch
528 OldIrql
= KeGetCurrentIrql();
529 ASSERT(OldIrql
<= APC_LEVEL
);
530 ASSERT(KeAreAllApcsDisabled() == TRUE
);
533 // Grab a copy of the PTE
535 TempPte
= *PointerPte
;
537 /* Do we have a prototype PTE? */
540 /* This should never happen */
541 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
543 /* Check if this is a kernel-mode address */
544 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
545 if (Address
>= MmSystemRangeStart
)
547 /* Lock the PFN database */
548 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
550 /* Has the PTE been made valid yet? */
551 if (!SuperProtoPte
->u
.Hard
.Valid
)
558 /* Resolve the fault -- this will release the PFN lock */
559 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
560 Status
= MiResolveProtoPteFault(StoreInstruction
,
570 ASSERT(Status
== STATUS_SUCCESS
);
572 /* Complete this as a transition fault */
573 ASSERT(OldIrql
== KeGetCurrentIrql());
574 ASSERT(OldIrql
<= APC_LEVEL
);
575 ASSERT(KeAreAllApcsDisabled() == TRUE
);
581 /* We currently only handle very limited paths */
582 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
583 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
585 /* Lock the PFN database */
586 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
588 /* For our current usage, this should be true */
589 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
590 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
592 /* Resolve the fault -- this will release the PFN lock */
593 Status
= MiResolveProtoPteFault(StoreInstruction
,
603 ASSERT(Status
== STATUS_SUCCESS
);
605 /* Complete this as a transition fault */
606 ASSERT(OldIrql
== KeGetCurrentIrql());
607 ASSERT(OldIrql
<= APC_LEVEL
);
608 ASSERT(KeAreAllApcsDisabled() == TRUE
);
609 return STATUS_PAGE_FAULT_TRANSITION
;
614 // The PTE must be invalid
616 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
618 /* Check if the PTE is completely empty */
619 if (TempPte
.u
.Long
== 0)
621 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
624 (ULONG_PTR
)TrapInformation
,
629 // No prototype, transition or page file software PTEs in ARM3 yet
631 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
632 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
633 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
636 // If we got this far, the PTE can only be a demand zero PTE, which is what
637 // we want. Go handle it!
639 Status
= MiResolveDemandZeroFault(Address
,
640 (ULONG
)PointerPte
->u
.Soft
.Protection
,
643 ASSERT(KeAreAllApcsDisabled() == TRUE
);
644 if (NT_SUCCESS(Status
))
647 // Make sure we're returning in a sane state and pass the status down
649 ASSERT(OldIrql
== KeGetCurrentIrql());
650 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
655 // Generate an access fault
657 return STATUS_ACCESS_VIOLATION
;
662 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
664 IN KPROCESSOR_MODE Mode
,
665 IN PVOID TrapInformation
)
667 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
668 PMMPTE ProtoPte
= NULL
;
669 PMMPTE PointerPte
= MiAddressToPte(Address
);
670 PMMPDE PointerPde
= MiAddressToPde(Address
);
671 #if (_MI_PAGING_LEVELS >= 3)
672 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
673 #if (_MI_PAGING_LEVELS == 4)
674 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
678 PETHREAD CurrentThread
;
679 PEPROCESS CurrentProcess
;
681 PMMSUPPORT WorkingSet
;
682 ULONG ProtectionCode
;
684 PFN_NUMBER PageFrameIndex
;
687 DPRINT("ARM3 FAULT AT: %p\n", Address
);
689 /* Check for page fault on high IRQL */
690 if (OldIrql
> APC_LEVEL
)
692 // There are some special cases where this is okay, but not in ARM3 yet
693 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
696 ASSERT(OldIrql
<= APC_LEVEL
);
699 /* Check for kernel fault address */
700 if (Address
>= MmSystemRangeStart
)
702 /* Bail out, if the fault came from user mode */
703 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
705 /* PXEs and PPEs for kernel mode are mapped for everything we need */
706 #if (_MI_PAGING_LEVELS >= 3)
708 #if (_MI_PAGING_LEVELS == 4)
709 (PointerPxe
->u
.Hard
.Valid
== 0) ||
711 (PointerPpe
->u
.Hard
.Valid
== 0))
713 /* The address is not from any pageable area! */
714 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
717 (ULONG_PTR
)TrapInformation
,
722 #if (_MI_PAGING_LEVELS == 2)
723 /* Check if we have a situation that might need synchronization
724 of the PDE with the system page directory */
725 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
727 /* This could be a paged pool commit with an unsychronized PDE.
728 NOTE: This way it works on x86, verify for other architectures! */
729 if (MiSynchronizeSystemPde((PMMPDE
)PointerPte
)) return STATUS_SUCCESS
;
733 /* Check if the PDE is invalid */
734 if (PointerPde
->u
.Hard
.Valid
== 0)
736 #if (_MI_PAGING_LEVELS == 2)
737 /* Sync this PDE and check, if that made it valid */
738 if (!MiSynchronizeSystemPde(PointerPde
))
741 /* PDE (still) not valid, kill the system */
742 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
745 (ULONG_PTR
)TrapInformation
,
750 /* The PDE is valid, so read the PTE */
751 TempPte
= *PointerPte
;
752 if (TempPte
.u
.Hard
.Valid
== 1)
755 // Only two things can go wrong here:
756 // Executing NX page (we couldn't care less)
757 // Writing to a read-only page (the stuff ARM3 works with is write,
758 // so again, moot point).
762 // Otherwise, the PDE was probably invalid, and all is good now
764 return STATUS_SUCCESS
;
767 // Check for a fault on the page table or hyperspace
768 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
770 ASSERT(TempPte
.u
.Long
!= (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
));
771 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
773 /* Use the process working set */
774 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
775 WorkingSet
= &CurrentProcess
->Vm
;
779 /* Otherwise use the system working set */
780 WorkingSet
= &MmSystemCacheWs
;
781 CurrentProcess
= NULL
;
784 /* Get the current thread */
785 CurrentThread
= PsGetCurrentThread();
787 /* Acquire the working set lock */
788 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
789 MiLockWorkingSet(CurrentThread
, WorkingSet
);
791 /* Re-read PTE now that we own the lock */
792 TempPte
= *PointerPte
;
793 if (TempPte
.u
.Hard
.Valid
== 1)
795 // Only two things can go wrong here:
796 // Executing NX page (we couldn't care less)
797 // Writing to a read-only page (the stuff ARM3 works with is write,
798 // so again, moot point).
799 ASSERT(TempPte
.u
.Hard
.Write
== 1);
801 /* Release the working set */
802 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
803 KeLowerIrql(LockIrql
);
805 // Otherwise, the PDE was probably invalid, and all is good now
806 return STATUS_SUCCESS
;
809 /* Check one kind of prototype PTE */
810 if (TempPte
.u
.Soft
.Prototype
)
812 /* Make sure protected pool is on, and that this is a pool address */
813 if ((MmProtectFreedNonPagedPool
) &&
814 (((Address
>= MmNonPagedPoolStart
) &&
815 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
816 MmSizeOfNonPagedPoolInBytes
))) ||
817 ((Address
>= MmNonPagedPoolExpansionStart
) &&
818 (Address
< MmNonPagedPoolEnd
))))
820 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
821 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
828 /* Get the prototype PTE! */
829 ProtoPte
= MiProtoPteToPte(&TempPte
);
833 /* We don't implement transition PTEs */
834 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
836 /* Check for no-access PTE */
837 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
839 /* Bugcheck the system! */
840 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
843 (ULONG_PTR
)TrapInformation
,
847 /* Check for demand page */
848 if ((StoreInstruction
) && !(TempPte
.u
.Hard
.Valid
))
850 /* Get the protection code */
851 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
853 /* Bugcheck the system! */
854 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
857 (ULONG_PTR
)TrapInformation
,
863 /* Now do the real fault handling */
864 Status
= MiDispatchFault(StoreInstruction
,
873 /* Release the working set */
874 ASSERT(KeAreAllApcsDisabled() == TRUE
);
875 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
876 KeLowerIrql(LockIrql
);
879 DPRINT("Fault resolved with status: %lx\n", Status
);
883 /* This is a user fault */
884 CurrentThread
= PsGetCurrentThread();
885 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
887 /* Lock the working set */
888 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
890 #if (_MI_PAGING_LEVELS == 2)
891 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
894 /* Check if this address range belongs to a valid allocation (VAD) */
895 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
896 if (ProtectionCode
== MM_NOACCESS
)
898 /* This is a bogus VA */
899 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
900 return STATUS_ACCESS_VIOLATION
;
903 #if (_MI_PAGING_LEVELS == 4)
904 /* On these systems we have PXEs and PPEs ready for everything we need */
905 if (PointerPxe
->u
.Hard
.Valid
== 0) return STATUS_ACCESS_VIOLATION
;
908 #if (_MI_PAGING_LEVELS >= 3)
909 if (PointerPpe
->u
.Hard
.Valid
== 0) return STATUS_ACCESS_VIOLATION
;
913 /* First things first, is the PDE valid? */
914 if (PointerPde
->u
.Hard
.Valid
== 0)
916 /* Right now, we only handle scenarios where the PDE is totally empty */
917 ASSERT(PointerPde
->u
.Long
== 0);
919 /* Right now, we expect a valid protection mask on the VAD */
920 ASSERT(ProtectionCode
!= MM_NOACCESS
);
922 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
926 /* Resolve a demand zero fault */
927 Status
= MiResolveDemandZeroFault(PointerPte
,
932 UserPdeFault
= FALSE
;
934 /* We should come back with APCs enabled, and with a valid PDE */
935 ASSERT(KeAreAllApcsDisabled() == TRUE
);
936 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
939 /* Now capture the PTE. Ignore virtual faults for now */
940 TempPte
= *PointerPte
;
941 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
943 /* Quick check for demand-zero */
944 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
946 /* Resolve the fault */
947 MI_WRITE_INVALID_PDE(PointerPde
, DemandZeroPde
);
948 MiResolveDemandZeroFault(Address
,
949 (ULONG
)PointerPte
->u
.Soft
.Protection
,
953 /* Return the status */
954 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
955 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
958 /* Make sure it's not a prototype PTE */
959 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
961 /* Check for non-demand zero PTE */
962 if (TempPte
.u
.Long
!= 0)
964 /* This is a page fault */
966 /* FIXME: Run MiAccessCheck */
968 /* Dispatch the fault */
969 Status
= MiDispatchFault(StoreInstruction
,
974 PsGetCurrentProcess(),
978 /* Return the status */
979 ASSERT(NT_SUCCESS(Status
));
980 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
981 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
987 /* Add an additional page table reference */
988 MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
989 ASSERT(MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
992 /* Did we get a prototype PTE back? */
995 /* No, create a new PTE. First, write the protection */
996 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
998 /* Lock the PFN database since we're going to grab a page */
999 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1001 /* Try to get a zero page */
1002 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1003 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1004 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1005 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1006 if (!PageFrameIndex
)
1008 /* Grab a page out of there. Later we should grab a colored zero page */
1009 PageFrameIndex
= MiRemoveAnyPage(Color
);
1010 ASSERT(PageFrameIndex
);
1012 /* Release the lock since we need to do some zeroing */
1013 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1015 /* Zero out the page, since it's for user-mode */
1016 MiZeroPfn(PageFrameIndex
);
1018 /* Grab the lock again so we can initialize the PFN entry */
1019 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1022 /* Initialize the PFN entry now */
1023 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1025 /* One more demand-zero fault */
1026 KeGetCurrentPrcb()->MmDemandZeroCount
++;
1028 /* And we're done with the lock */
1029 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1031 /* User fault, build a user PTE */
1032 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1034 PointerPte
->u
.Soft
.Protection
,
1037 /* Write the dirty bit for writeable pages */
1038 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1040 /* And now write down the PTE, making the address valid */
1041 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1044 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
1048 /* No guard page support yet */
1049 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1050 ASSERT(ProtectionCode
!= 0x100);
1052 /* Write the prototype PTE */
1053 TempPte
= PrototypePte
;
1054 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1055 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1057 /* Handle the fault */
1058 Status
= MiDispatchFault(StoreInstruction
,
1066 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
1067 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1068 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
!= 0);
1071 /* Release the working set */
1072 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);