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
);
187 MiCheckPdeForPagedPool(IN PVOID Address
)
189 return STATUS_ACCESS_VIOLATION
;
195 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
202 /* Get the PFN for this page */
203 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
206 /* Grab a system PTE we can use to zero the page */
207 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
210 /* Initialize the PTE for it */
211 TempPte
= ValidKernelPte
;
212 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
215 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
217 /* Write combining, no caching */
218 MI_PAGE_DISABLE_CACHE(&TempPte
);
219 MI_PAGE_WRITE_COMBINED(&TempPte
);
221 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
223 /* Write through, no caching */
224 MI_PAGE_DISABLE_CACHE(&TempPte
);
225 MI_PAGE_WRITE_THROUGH(&TempPte
);
228 /* Make the system PTE valid with our PFN */
229 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
231 /* Get the address it maps to, and zero it out */
232 ZeroAddress
= MiPteToAddress(ZeroPte
);
233 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
235 /* Now get rid of it */
236 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
241 MiResolveDemandZeroFault(IN PVOID Address
,
243 IN PEPROCESS Process
,
246 PFN_NUMBER PageFrameNumber
= 0;
247 PMMPTE PointerPte
= MiAddressToPte(Address
);
249 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
251 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
255 /* Must currently only be called by paging path */
256 if ((Process
) && (OldIrql
== MM_NOIRQL
))
259 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
262 ASSERT(Process
->ForkInProgress
== NULL
);
264 /* Get process color */
265 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
266 ASSERT(Color
!= 0xFFFFFFFF);
268 /* We'll need a zero page */
273 /* Check if we need a zero page */
274 NeedZero
= (OldIrql
!= MM_NOIRQL
);
276 /* Get the next system page color */
277 Color
= MI_GET_NEXT_COLOR();
280 /* Check if the PFN database should be acquired */
281 if (OldIrql
== MM_NOIRQL
)
283 /* Acquire it and remember we should release it after */
284 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
288 /* We either manually locked the PFN DB, or already came with it locked */
289 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
291 /* Do we need a zero page? */
292 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
294 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
295 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
297 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
298 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
299 if ((NeedZero
) && (Process
))
301 /* Try to get one, if we couldn't grab a free page and zero it */
302 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
305 /* We got a genuine zero page, stop worrying about it */
310 /* We'll need a free page and zero it manually */
311 PageFrameNumber
= MiRemoveAnyPage(Color
);
316 /* Process or system doesn't want a zero page, grab anything */
317 PageFrameNumber
= MiRemoveAnyPage(Color
);
321 /* System wants a zero page, obtain one */
322 PageFrameNumber
= MiRemoveZeroPage(Color
);
326 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
328 /* Increment demand zero faults */
329 KeGetCurrentPrcb()->MmDemandZeroCount
++;
331 /* Release PFN lock if needed */
332 if (HaveLock
) KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
334 /* Zero the page if need be */
335 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
338 MI_MAKE_HARDWARE_PTE(&TempPte
,
343 /* Set it dirty if it's a writable page */
344 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
347 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
352 DPRINT("Paged pool page has now been paged in\n");
353 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
358 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
360 IN PMMPTE PointerPte
,
361 IN PMMPTE PointerProtoPte
,
367 ULONG_PTR Protection
;
368 PFN_NUMBER PageFrameIndex
;
370 /* Must be called with an valid prototype PTE, with the PFN lock held */
371 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
372 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
375 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
377 /* Get the PFN entry and set it as a prototype PTE */
378 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
379 Pfn1
->u3
.e1
.PrototypePte
= 1;
381 /* FIXME: Increment the share count for the page table */
383 /* Check where we should be getting the protection information from */
384 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
386 /* Get the protection from the PTE, there's no real Proto PTE data */
387 Protection
= PointerPte
->u
.Soft
.Protection
;
391 /* Get the protection from the original PTE link */
392 OriginalPte
= &Pfn1
->OriginalPte
;
393 Protection
= OriginalPte
->u
.Soft
.Protection
;
396 /* Release the PFN lock */
397 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
399 /* Remove caching bits */
400 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
402 /* Check if this is a kernel or user address */
403 if (Address
< MmSystemRangeStart
)
405 /* Build the user PTE */
406 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
410 /* Build the kernel PTE */
411 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
415 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
418 return STATUS_SUCCESS
;
423 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
425 IN PMMPTE PointerPte
,
426 IN PMMPTE PointerProtoPte
,
427 IN OUT PMMPFN
*OutPfn
,
428 OUT PVOID
*PageFileData
,
430 IN PEPROCESS Process
,
432 IN PVOID TrapInformation
)
436 PFN_NUMBER PageFrameIndex
;
439 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
440 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
441 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
442 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
444 /* Read the prototype PTE and check if it's valid */
445 TempPte
= *PointerProtoPte
;
446 if (TempPte
.u
.Hard
.Valid
== 1)
448 /* One more user of this mapped page */
449 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
450 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
451 Pfn1
->u2
.ShareCount
++;
453 /* Call it a transition */
454 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
456 /* Complete the prototype PTE fault -- this will release the PFN lock */
457 return MiCompleteProtoPteFault(StoreInstruction
,
465 /* Make sure there's some protection mask */
466 if (TempPte
.u
.Long
== 0)
468 /* Release the lock */
469 DPRINT1("Access on reserved section?\n");
470 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
471 return STATUS_ACCESS_VIOLATION
;
474 /* This is the only thing we support right now */
475 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
476 ASSERT(TempPte
.u
.Proto
.ReadOnly
== 0);
477 ASSERT(PointerPte
> MiHighestUserPte
);
478 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
479 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
481 /* Resolve the demand zero fault */
482 Status
= MiResolveDemandZeroFault(Address
,
483 (ULONG
)PointerProtoPte
->u
.Soft
.Protection
,
486 ASSERT(NT_SUCCESS(Status
));
488 /* Complete the prototype PTE fault -- this will release the PFN lock */
489 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
490 return MiCompleteProtoPteFault(StoreInstruction
,
500 MiDispatchFault(IN BOOLEAN StoreInstruction
,
502 IN PMMPTE PointerPte
,
503 IN PMMPTE PointerProtoPte
,
504 IN BOOLEAN Recursive
,
505 IN PEPROCESS Process
,
506 IN PVOID TrapInformation
,
510 KIRQL OldIrql
, LockIrql
;
512 PMMPTE SuperProtoPte
;
513 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
517 /* Make sure the addresses are ok */
518 ASSERT(PointerPte
== MiAddressToPte(Address
));
521 // Make sure APCs are off and we're not at dispatch
523 OldIrql
= KeGetCurrentIrql();
524 ASSERT(OldIrql
<= APC_LEVEL
);
525 ASSERT(KeAreAllApcsDisabled() == TRUE
);
528 // Grab a copy of the PTE
530 TempPte
= *PointerPte
;
532 /* Do we have a prototype PTE? */
535 /* This should never happen */
536 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
538 /* Check if this is a kernel-mode address */
539 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
540 if (Address
>= MmSystemRangeStart
)
542 /* Lock the PFN database */
543 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
545 /* Has the PTE been made valid yet? */
546 if (!SuperProtoPte
->u
.Hard
.Valid
)
553 /* Resolve the fault -- this will release the PFN lock */
554 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
555 Status
= MiResolveProtoPteFault(StoreInstruction
,
565 ASSERT(Status
== STATUS_SUCCESS
);
567 /* Complete this as a transition fault */
568 ASSERT(OldIrql
== KeGetCurrentIrql());
569 ASSERT(OldIrql
<= APC_LEVEL
);
570 ASSERT(KeAreAllApcsDisabled() == TRUE
);
576 /* We currently only handle very limited paths */
577 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
578 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
580 /* Lock the PFN database */
581 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
583 /* For our current usage, this should be true */
584 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
585 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
587 /* Resolve the fault -- this will release the PFN lock */
588 Status
= MiResolveProtoPteFault(StoreInstruction
,
598 ASSERT(Status
== STATUS_SUCCESS
);
600 /* Complete this as a transition fault */
601 ASSERT(OldIrql
== KeGetCurrentIrql());
602 ASSERT(OldIrql
<= APC_LEVEL
);
603 ASSERT(KeAreAllApcsDisabled() == TRUE
);
604 return STATUS_PAGE_FAULT_TRANSITION
;
609 // The PTE must be invalid
611 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
613 /* Check if the PTE is completely empty */
614 if (TempPte
.u
.Long
== 0)
616 /* The address is not from any pageable area! */
617 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
620 (ULONG_PTR
)TrapInformation
,
625 // No prototype, transition or page file software PTEs in ARM3 yet
627 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
628 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
629 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
632 // If we got this far, the PTE can only be a demand zero PTE, which is what
633 // we want. Go handle it!
635 Status
= MiResolveDemandZeroFault(Address
,
636 (ULONG
)PointerPte
->u
.Soft
.Protection
,
639 ASSERT(KeAreAllApcsDisabled() == TRUE
);
640 if (NT_SUCCESS(Status
))
643 // Make sure we're returning in a sane state and pass the status down
645 ASSERT(OldIrql
== KeGetCurrentIrql());
646 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
651 // Generate an access fault
653 return STATUS_ACCESS_VIOLATION
;
658 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
660 IN KPROCESSOR_MODE Mode
,
661 IN PVOID TrapInformation
)
663 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
664 PMMPTE ProtoPte
= NULL
;
665 PMMPTE PointerPte
= MiAddressToPte(Address
);
666 PMMPDE PointerPde
= MiAddressToPde(Address
);
667 #if (_MI_PAGING_LEVELS >= 3)
668 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
669 #if (_MI_PAGING_LEVELS == 4)
670 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
674 PETHREAD CurrentThread
;
675 PEPROCESS CurrentProcess
;
677 PMMSUPPORT WorkingSet
;
678 ULONG ProtectionCode
;
680 PFN_NUMBER PageFrameIndex
;
683 DPRINT("ARM3 FAULT AT: %p\n", Address
);
685 /* Check for page fault on high IRQL */
686 if (OldIrql
> APC_LEVEL
)
688 // There are some special cases where this is okay, but not in ARM3 yet
689 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
692 ASSERT(OldIrql
<= APC_LEVEL
);
695 /* Check for kernel fault address */
696 if (Address
>= MmSystemRangeStart
)
698 /* Bail out, if the fault came from user mode */
699 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
701 /* PXEs and PPEs for kernel mode are mapped for everything we need */
702 #if (_MI_PAGING_LEVELS >= 3)
704 #if (_MI_PAGING_LEVELS == 4)
705 (PointerPxe
->u
.Hard
.Valid
== 0) ||
707 (PointerPpe
->u
.Hard
.Valid
== 0))
709 /* The address is not from any pageable area! */
710 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
713 (ULONG_PTR
)TrapInformation
,
718 #if (_MI_PAGING_LEVELS == 2)
719 /* Check if we have a situation that might need synchronization
720 of the PDE with the system page directory */
721 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
723 /* This could be a paged pool commit with an unsychronized PDE.
724 NOTE: This way it works on x86, verify for other architectures! */
725 if (MiSynchronizeSystemPde((PMMPDE
)PointerPte
)) return STATUS_SUCCESS
;
729 /* Check if the PDE is invalid */
730 if (PointerPde
->u
.Hard
.Valid
== 0)
732 #if (_MI_PAGING_LEVELS == 2)
733 /* Sync this PDE and check, if that made it valid */
734 if (!MiSynchronizeSystemPde(PointerPde
))
737 /* PDE (still) not valid, kill the system */
738 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
741 (ULONG_PTR
)TrapInformation
,
746 /* The PDE is valid, so read the PTE */
747 TempPte
= *PointerPte
;
748 if (TempPte
.u
.Hard
.Valid
== 1)
751 // Only two things can go wrong here:
752 // Executing NX page (we couldn't care less)
753 // Writing to a read-only page (the stuff ARM3 works with is write,
754 // so again, moot point).
758 // Otherwise, the PDE was probably invalid, and all is good now
760 return STATUS_SUCCESS
;
763 /* Get the current thread */
764 CurrentThread
= PsGetCurrentThread();
766 // Check for a fault on the page table or hyperspace
767 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
769 ASSERT(TempPte
.u
.Long
!= (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
));
770 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
772 /* Use the process working set */
773 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
774 WorkingSet
= &CurrentProcess
->Vm
;
778 /* Otherwise use the system working set */
779 WorkingSet
= &MmSystemCacheWs
;
780 CurrentProcess
= NULL
;
783 /* Acquire the working set lock */
784 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
785 MiLockWorkingSet(CurrentThread
, WorkingSet
);
787 /* Re-read PTE now that we own the lock */
788 TempPte
= *PointerPte
;
789 if (TempPte
.u
.Hard
.Valid
== 1)
791 // Only two things can go wrong here:
792 // Executing NX page (we couldn't care less)
793 // Writing to a read-only page (the stuff ARM3 works with is write,
794 // so again, moot point).
795 ASSERT(TempPte
.u
.Hard
.Write
== 1);
797 /* Release the working set */
798 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
799 KeLowerIrql(LockIrql
);
801 // Otherwise, the PDE was probably invalid, and all is good now
802 return STATUS_SUCCESS
;
805 /* Check one kind of prototype PTE */
806 if (TempPte
.u
.Soft
.Prototype
)
808 /* Make sure protected pool is on, and that this is a pool address */
809 if ((MmProtectFreedNonPagedPool
) &&
810 (((Address
>= MmNonPagedPoolStart
) &&
811 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
812 MmSizeOfNonPagedPoolInBytes
))) ||
813 ((Address
>= MmNonPagedPoolExpansionStart
) &&
814 (Address
< MmNonPagedPoolEnd
))))
816 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
817 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
824 /* Get the prototype PTE! */
825 ProtoPte
= MiProtoPteToPte(&TempPte
);
829 /* We don't implement transition PTEs */
830 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
832 /* Check for no-access PTE */
833 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
835 /* Bugcheck the system! */
836 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
839 (ULONG_PTR
)TrapInformation
,
843 /* Check for demand page */
844 if ((StoreInstruction
) && !(TempPte
.u
.Hard
.Valid
))
846 /* Get the protection code */
847 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
849 /* Bugcheck the system! */
850 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
853 (ULONG_PTR
)TrapInformation
,
859 /* Now do the real fault handling */
860 Status
= MiDispatchFault(StoreInstruction
,
869 /* Release the working set */
870 ASSERT(KeAreAllApcsDisabled() == TRUE
);
871 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
872 KeLowerIrql(LockIrql
);
875 DPRINT("Fault resolved with status: %lx\n", Status
);
879 /* This is a user fault */
880 CurrentThread
= PsGetCurrentThread();
881 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
883 /* Lock the working set */
884 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
886 #if (_MI_PAGING_LEVELS == 2)
887 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
890 /* Check if this address range belongs to a valid allocation (VAD) */
891 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
892 if (ProtectionCode
== MM_NOACCESS
)
894 /* This is a bogus VA */
895 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
896 return STATUS_ACCESS_VIOLATION
;
899 #if (_MI_PAGING_LEVELS == 4)
900 /* Check if the PXE is valid */
901 if (PointerPxe
->u
.Hard
.Valid
== 0)
903 /* Right now, we only handle scenarios where the PXE is totally empty */
904 ASSERT(PointerPxe
->u
.Long
== 0);
906 /* Resolve a demand zero fault */
907 Status
= MiResolveDemandZeroFault(PointerPpe
,
912 /* We should come back with a valid PXE */
913 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
917 #if (_MI_PAGING_LEVELS >= 3)
918 /* Check if the PPE is valid */
919 if (PointerPpe
->u
.Hard
.Valid
== 0)
921 /* Right now, we only handle scenarios where the PPE is totally empty */
922 ASSERT(PointerPpe
->u
.Long
== 0);
924 /* Resolve a demand zero fault */
925 Status
= MiResolveDemandZeroFault(PointerPde
,
930 /* We should come back with a valid PPE */
931 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
935 /* Check if the PDE is valid */
936 if (PointerPde
->u
.Hard
.Valid
== 0)
938 /* Right now, we only handle scenarios where the PDE is totally empty */
939 ASSERT(PointerPde
->u
.Long
== 0);
941 /* Right now, we expect a valid protection mask on the VAD */
942 ASSERT(ProtectionCode
!= MM_NOACCESS
);
944 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
948 /* Resolve a demand zero fault */
949 Status
= MiResolveDemandZeroFault(PointerPte
,
954 UserPdeFault
= FALSE
;
956 /* We should come back with APCs enabled, and with a valid PDE */
957 ASSERT(KeAreAllApcsDisabled() == TRUE
);
958 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
961 /* Now capture the PTE. Ignore virtual faults for now */
962 TempPte
= *PointerPte
;
963 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
965 /* Quick check for demand-zero */
966 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
968 /* Resolve the fault */
969 MI_WRITE_INVALID_PDE(PointerPde
, DemandZeroPde
);
970 MiResolveDemandZeroFault(Address
,
971 (ULONG
)PointerPte
->u
.Soft
.Protection
,
975 /* Return the status */
976 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
977 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
980 /* Make sure it's not a prototype PTE */
981 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
983 /* Check for non-demand zero PTE */
984 if (TempPte
.u
.Long
!= 0)
986 /* This is a page fault */
988 /* FIXME: Run MiAccessCheck */
990 /* Dispatch the fault */
991 Status
= MiDispatchFault(StoreInstruction
,
996 PsGetCurrentProcess(),
1000 /* Return the status */
1001 ASSERT(NT_SUCCESS(Status
));
1002 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1003 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1007 #if (_MI_PAGING_LEVELS == 2)
1008 /* Add an additional page table reference */
1009 MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
1010 ASSERT(MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
1013 /* Did we get a prototype PTE back? */
1016 /* No, create a new PTE. First, write the protection */
1017 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1019 /* Lock the PFN database since we're going to grab a page */
1020 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1022 /* Try to get a zero page */
1023 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1024 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1025 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1026 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1027 if (!PageFrameIndex
)
1029 /* Grab a page out of there. Later we should grab a colored zero page */
1030 PageFrameIndex
= MiRemoveAnyPage(Color
);
1031 ASSERT(PageFrameIndex
);
1033 /* Release the lock since we need to do some zeroing */
1034 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1036 /* Zero out the page, since it's for user-mode */
1037 MiZeroPfn(PageFrameIndex
);
1039 /* Grab the lock again so we can initialize the PFN entry */
1040 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1043 /* Initialize the PFN entry now */
1044 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1046 /* One more demand-zero fault */
1047 KeGetCurrentPrcb()->MmDemandZeroCount
++;
1049 /* And we're done with the lock */
1050 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1052 /* User fault, build a user PTE */
1053 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1055 PointerPte
->u
.Soft
.Protection
,
1058 /* Write the dirty bit for writeable pages */
1059 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1061 /* And now write down the PTE, making the address valid */
1062 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1065 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
1069 /* No guard page support yet */
1070 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1071 ASSERT(ProtectionCode
!= 0x100);
1073 /* Write the prototype PTE */
1074 TempPte
= PrototypePte
;
1075 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1076 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1078 /* Handle the fault */
1079 Status
= MiDispatchFault(StoreInstruction
,
1087 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
1088 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1089 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
!= 0);
1092 /* Release the working set */
1093 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1099 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1102 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1103 KLOCK_QUEUE_HANDLE ProcessLock
;
1104 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1105 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1107 /* Only accept valid flags */
1108 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1111 DPRINT1("Invalid no-execute options\n");
1112 return STATUS_INVALID_PARAMETER
;
1115 /* Change the NX state in the process lock */
1116 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1118 /* Don't change anything if the permanent flag was set */
1119 if (!CurrentProcess
->Flags
.Permanent
)
1121 /* Start by assuming it's not disabled */
1122 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1124 /* Now process each flag and turn the equivalent bit on */
1125 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1127 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1129 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1131 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1133 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1135 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1137 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1139 CurrentProcess
->Flags
.Permanent
= TRUE
;
1141 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1143 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1145 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1147 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1150 /* These are turned on by default if no-execution is also eanbled */
1151 if (CurrentProcess
->Flags
.ExecuteEnable
)
1153 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1154 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1158 Status
= STATUS_SUCCESS
;
1161 /* Release the lock and return status */
1162 KiReleaseProcessLock(&ProcessLock
);