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
)
81 /* This must be a TEB/PEB VAD */
82 if (Vad
->u
.VadFlags
.MemCommit
)
84 /* It's committed, so return the VAD protection */
85 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
89 /* It has not yet been committed, so return no access */
90 *ProtectCode
= MM_NOACCESS
;
96 /* Return the proto VAD */
97 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== 0);
100 /* Get the prototype PTE for this page */
101 PointerPte
= (((ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
) - Vad
->StartingVpn
) + Vad
->FirstPrototypePte
;
102 ASSERT(PointerPte
<= Vad
->LastContiguousPte
);
103 ASSERT(PointerPte
!= NULL
);
105 /* Return the Prototype PTE and the protection for the page mapping */
106 *ProtectCode
= (ULONG
)Vad
->u
.VadFlags
.Protection
;
111 #if (_MI_PAGING_LEVELS == 2)
114 MiSynchronizeSystemPde(PMMPDE PointerPde
)
119 /* Get the Index from the PDE */
120 Index
= ((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
);
122 /* Copy the PDE from the double-mapped system page directory */
123 SystemPde
= MmSystemPagePtes
[Index
];
124 *PointerPde
= SystemPde
;
126 /* Make sure we re-read the PDE and PTE */
127 KeMemoryBarrierWithoutFence();
129 /* Return, if we had success */
130 return (BOOLEAN
)SystemPde
.u
.Hard
.Valid
;
135 MiCheckPdeForPagedPool(IN PVOID Address
)
138 NTSTATUS Status
= STATUS_SUCCESS
;
140 /* No session support in ReactOS yet */
141 ASSERT(MI_IS_SESSION_ADDRESS(Address
) == FALSE
);
142 ASSERT(MI_IS_SESSION_PTE(Address
) == FALSE
);
145 // Check if this is a fault while trying to access the page table itself
147 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
150 // Send a hint to the page fault handler that this is only a valid fault
151 // if we already detected this was access within the page table range
153 PointerPde
= (PMMPDE
)MiAddressToPte(Address
);
154 Status
= STATUS_WAIT_1
;
156 else if (Address
< MmSystemRangeStart
)
159 // This is totally illegal
161 return STATUS_ACCESS_VIOLATION
;
166 // Get the PDE for the address
168 PointerPde
= MiAddressToPde(Address
);
172 // Check if it's not valid
174 if (PointerPde
->u
.Hard
.Valid
== 0)
180 // Copy it from our double-mapped system page directory
182 InterlockedExchangePte(PointerPde
,
183 MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)].u
.Long
);
195 MiCheckPdeForPagedPool(IN PVOID Address
)
197 return STATUS_ACCESS_VIOLATION
;
203 MiZeroPfn(IN PFN_NUMBER PageFrameNumber
)
210 /* Get the PFN for this page */
211 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
214 /* Grab a system PTE we can use to zero the page */
215 ZeroPte
= MiReserveSystemPtes(1, SystemPteSpace
);
218 /* Initialize the PTE for it */
219 TempPte
= ValidKernelPte
;
220 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
223 if (Pfn1
->u3
.e1
.CacheAttribute
== MiWriteCombined
)
225 /* Write combining, no caching */
226 MI_PAGE_DISABLE_CACHE(&TempPte
);
227 MI_PAGE_WRITE_COMBINED(&TempPte
);
229 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
231 /* Write through, no caching */
232 MI_PAGE_DISABLE_CACHE(&TempPte
);
233 MI_PAGE_WRITE_THROUGH(&TempPte
);
236 /* Make the system PTE valid with our PFN */
237 MI_WRITE_VALID_PTE(ZeroPte
, TempPte
);
239 /* Get the address it maps to, and zero it out */
240 ZeroAddress
= MiPteToAddress(ZeroPte
);
241 KeZeroPages(ZeroAddress
, PAGE_SIZE
);
243 /* Now get rid of it */
244 MiReleaseSystemPtes(ZeroPte
, 1, SystemPteSpace
);
249 MiResolveDemandZeroFault(IN PVOID Address
,
250 IN PMMPTE PointerPte
,
251 IN PEPROCESS Process
,
254 PFN_NUMBER PageFrameNumber
= 0;
256 BOOLEAN NeedZero
= FALSE
, HaveLock
= FALSE
;
258 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
262 /* Must currently only be called by paging path */
263 if ((Process
) && (OldIrql
== MM_NOIRQL
))
266 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte
));
269 ASSERT(Process
->ForkInProgress
== NULL
);
271 /* Get process color */
272 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
273 ASSERT(Color
!= 0xFFFFFFFF);
275 /* We'll need a zero page */
280 /* Check if we need a zero page */
281 NeedZero
= (OldIrql
!= MM_NOIRQL
);
283 /* Get the next system page color */
284 Color
= MI_GET_NEXT_COLOR();
287 /* Check if the PFN database should be acquired */
288 if (OldIrql
== MM_NOIRQL
)
290 /* Acquire it and remember we should release it after */
291 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
295 /* We either manually locked the PFN DB, or already came with it locked */
296 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
298 /* Do we need a zero page? */
299 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
301 if (UserPdeFault
) MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
302 if (!UserPdeFault
) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO
);
304 if (Process
) MI_SET_PROCESS2(Process
->ImageFileName
);
305 if (!Process
) MI_SET_PROCESS2("Kernel Demand 0");
306 if ((NeedZero
) && (Process
))
308 /* Try to get one, if we couldn't grab a free page and zero it */
309 PageFrameNumber
= MiRemoveZeroPageSafe(Color
);
312 /* We got a genuine zero page, stop worrying about it */
317 /* We'll need a free page and zero it manually */
318 PageFrameNumber
= MiRemoveAnyPage(Color
);
323 /* Process or system doesn't want a zero page, grab anything */
324 PageFrameNumber
= MiRemoveAnyPage(Color
);
328 /* System wants a zero page, obtain one */
329 PageFrameNumber
= MiRemoveZeroPage(Color
);
334 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
336 /* Increment demand zero faults */
337 KeGetCurrentPrcb()->MmDemandZeroCount
++;
339 /* Release PFN lock if needed */
340 if (HaveLock
) KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
342 /* Zero the page if need be */
343 if (NeedZero
) MiZeroPfn(PageFrameNumber
);
345 /* Fault on user PDE, or fault on user PTE? */
346 if (PointerPte
<= MiHighestUserPte
)
348 /* User fault, build a user PTE */
349 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
351 PointerPte
->u
.Soft
.Protection
,
356 /* This is a user-mode PDE, create a kernel PTE for it */
357 MI_MAKE_HARDWARE_PTE(&TempPte
,
359 PointerPte
->u
.Soft
.Protection
,
363 /* Set it dirty if it's a writable page */
364 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
367 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
372 DPRINT("Demand zero page has now been paged in\n");
373 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
378 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction
,
380 IN PMMPTE PointerPte
,
381 IN PMMPTE PointerProtoPte
,
386 PMMPTE OriginalPte
, PageTablePte
;
387 ULONG_PTR Protection
;
388 PFN_NUMBER PageFrameIndex
;
391 /* Must be called with an valid prototype PTE, with the PFN lock held */
392 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
393 ASSERT(PointerProtoPte
->u
.Hard
.Valid
== 1);
396 PageFrameIndex
= PFN_FROM_PTE(PointerProtoPte
);
398 /* Get the PFN entry and set it as a prototype PTE */
399 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
400 Pfn1
->u3
.e1
.PrototypePte
= 1;
402 /* Increment the share count for the page table */
403 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
404 // This could be beause MiDeletePte is now being called from strange code in Rosmm
405 PageTablePte
= MiAddressToPte(PointerPte
);
406 Pfn2
= MiGetPfnEntry(PageTablePte
->u
.Hard
.PageFrameNumber
);
407 //Pfn2->u2.ShareCount++;
409 /* Check where we should be getting the protection information from */
410 if (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
412 /* Get the protection from the PTE, there's no real Proto PTE data */
413 Protection
= PointerPte
->u
.Soft
.Protection
;
417 /* Get the protection from the original PTE link */
418 OriginalPte
= &Pfn1
->OriginalPte
;
419 Protection
= OriginalPte
->u
.Soft
.Protection
;
422 /* Release the PFN lock */
423 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
425 /* Remove caching bits */
426 Protection
&= ~(MM_NOCACHE
| MM_NOACCESS
);
428 /* Check if this is a kernel or user address */
429 if (Address
< MmSystemRangeStart
)
431 /* Build the user PTE */
432 MI_MAKE_HARDWARE_PTE_USER(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
436 /* Build the kernel PTE */
437 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, Protection
, PageFrameIndex
);
441 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
444 return STATUS_SUCCESS
;
449 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction
,
451 IN PMMPTE PointerPte
,
452 IN PMMPTE PointerProtoPte
,
453 IN OUT PMMPFN
*OutPfn
,
454 OUT PVOID
*PageFileData
,
456 IN PEPROCESS Process
,
458 IN PVOID TrapInformation
)
462 PFN_NUMBER PageFrameIndex
;
465 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
466 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
467 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
468 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
470 /* Read the prototype PTE and check if it's valid */
471 TempPte
= *PointerProtoPte
;
472 if (TempPte
.u
.Hard
.Valid
== 1)
474 /* One more user of this mapped page */
475 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
476 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
477 Pfn1
->u2
.ShareCount
++;
479 /* Call it a transition */
480 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount
);
482 /* Complete the prototype PTE fault -- this will release the PFN lock */
483 return MiCompleteProtoPteFault(StoreInstruction
,
491 /* Make sure there's some protection mask */
492 if (TempPte
.u
.Long
== 0)
494 /* Release the lock */
495 DPRINT1("Access on reserved section?\n");
496 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
497 return STATUS_ACCESS_VIOLATION
;
500 /* This is the only thing we support right now */
501 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
502 ASSERT(TempPte
.u
.Proto
.ReadOnly
== 0);
503 ASSERT(PointerPte
> MiHighestUserPte
);
504 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
505 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
507 /* Resolve the demand zero fault */
508 Status
= MiResolveDemandZeroFault(Address
,
512 ASSERT(NT_SUCCESS(Status
));
514 /* Complete the prototype PTE fault -- this will release the PFN lock */
515 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
516 return MiCompleteProtoPteFault(StoreInstruction
,
526 MiDispatchFault(IN BOOLEAN StoreInstruction
,
528 IN PMMPTE PointerPte
,
529 IN PMMPTE PointerProtoPte
,
530 IN BOOLEAN Recursive
,
531 IN PEPROCESS Process
,
532 IN PVOID TrapInformation
,
536 KIRQL OldIrql
, LockIrql
;
538 PMMPTE SuperProtoPte
;
539 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
543 /* Make sure the addresses are ok */
544 ASSERT(PointerPte
== MiAddressToPte(Address
));
547 // Make sure APCs are off and we're not at dispatch
549 OldIrql
= KeGetCurrentIrql();
550 ASSERT(OldIrql
<= APC_LEVEL
);
551 ASSERT(KeAreAllApcsDisabled() == TRUE
);
554 // Grab a copy of the PTE
556 TempPte
= *PointerPte
;
558 /* Do we have a prototype PTE? */
561 /* This should never happen */
562 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte
));
564 /* Check if this is a kernel-mode address */
565 SuperProtoPte
= MiAddressToPte(PointerProtoPte
);
566 if (Address
>= MmSystemRangeStart
)
568 /* Lock the PFN database */
569 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
571 /* Has the PTE been made valid yet? */
572 if (!SuperProtoPte
->u
.Hard
.Valid
)
579 /* Resolve the fault -- this will release the PFN lock */
580 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
581 Status
= MiResolveProtoPteFault(StoreInstruction
,
591 ASSERT(Status
== STATUS_SUCCESS
);
593 /* Complete this as a transition fault */
594 ASSERT(OldIrql
== KeGetCurrentIrql());
595 ASSERT(OldIrql
<= APC_LEVEL
);
596 ASSERT(KeAreAllApcsDisabled() == TRUE
);
602 /* We currently only handle very limited paths */
603 ASSERT(PointerPte
->u
.Soft
.Prototype
== 1);
604 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
);
606 /* Lock the PFN database */
607 LockIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
609 /* For our current usage, this should be true */
610 ASSERT(SuperProtoPte
->u
.Hard
.Valid
== 1);
611 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
613 /* Resolve the fault -- this will release the PFN lock */
614 Status
= MiResolveProtoPteFault(StoreInstruction
,
624 ASSERT(Status
== STATUS_SUCCESS
);
626 /* Complete this as a transition fault */
627 ASSERT(OldIrql
== KeGetCurrentIrql());
628 ASSERT(OldIrql
<= APC_LEVEL
);
629 ASSERT(KeAreAllApcsDisabled() == TRUE
);
630 return STATUS_PAGE_FAULT_TRANSITION
;
635 // The PTE must be invalid but not completely empty. It must also not be a
636 // prototype PTE as that scenario should've been handled above
638 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
639 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
640 ASSERT(TempPte
.u
.Long
!= 0);
643 // No transition or page file software PTEs in ARM3 yet, so this must be a
646 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
647 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
650 // If we got this far, the PTE can only be a demand zero PTE, which is what
651 // we want. Go handle it!
653 Status
= MiResolveDemandZeroFault(Address
,
657 ASSERT(KeAreAllApcsDisabled() == TRUE
);
658 if (NT_SUCCESS(Status
))
661 // Make sure we're returning in a sane state and pass the status down
663 ASSERT(OldIrql
== KeGetCurrentIrql());
664 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
669 // Generate an access fault
671 return STATUS_ACCESS_VIOLATION
;
676 MmArmAccessFault(IN BOOLEAN StoreInstruction
,
678 IN KPROCESSOR_MODE Mode
,
679 IN PVOID TrapInformation
)
681 KIRQL OldIrql
= KeGetCurrentIrql(), LockIrql
;
682 PMMPTE ProtoPte
= NULL
;
683 PMMPTE PointerPte
= MiAddressToPte(Address
);
684 PMMPDE PointerPde
= MiAddressToPde(Address
);
685 #if (_MI_PAGING_LEVELS >= 3)
686 PMMPDE PointerPpe
= MiAddressToPpe(Address
);
687 #if (_MI_PAGING_LEVELS == 4)
688 PMMPDE PointerPxe
= MiAddressToPxe(Address
);
692 PETHREAD CurrentThread
;
693 PEPROCESS CurrentProcess
;
695 PMMSUPPORT WorkingSet
;
696 ULONG ProtectionCode
;
698 PFN_NUMBER PageFrameIndex
;
700 DPRINT("ARM3 FAULT AT: %p\n", Address
);
702 /* Check for page fault on high IRQL */
703 if (OldIrql
> APC_LEVEL
)
705 // There are some special cases where this is okay, but not in ARM3 yet
706 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
709 ASSERT(OldIrql
<= APC_LEVEL
);
712 /* Check for kernel fault address */
713 if (Address
>= MmSystemRangeStart
)
715 /* Bail out, if the fault came from user mode */
716 if (Mode
== UserMode
) return STATUS_ACCESS_VIOLATION
;
718 /* PXEs and PPEs for kernel mode are mapped for everything we need */
719 #if (_MI_PAGING_LEVELS >= 3)
721 #if (_MI_PAGING_LEVELS == 4)
722 (PointerPxe
->u
.Hard
.Valid
== 0) ||
724 (PointerPpe
->u
.Hard
.Valid
== 0))
726 /* The address is not from any pageable area! */
727 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
730 (ULONG_PTR
)TrapInformation
,
735 #if (_MI_PAGING_LEVELS == 2)
736 /* Check if we have a situation that might need synchronization
737 of the PDE with the system page directory */
738 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address
))
740 /* This could be a paged pool commit with an unsychronized PDE.
741 NOTE: This way it works on x86, verify for other architectures! */
742 if (MiSynchronizeSystemPde((PMMPDE
)PointerPte
)) return STATUS_SUCCESS
;
746 /* Check if the PDE is invalid */
747 if (PointerPde
->u
.Hard
.Valid
== 0)
749 #if (_MI_PAGING_LEVELS == 2)
750 /* Sync this PDE and check, if that made it valid */
751 if (!MiSynchronizeSystemPde(PointerPde
))
754 /* PDE (still) not valid, kill the system */
755 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
758 (ULONG_PTR
)TrapInformation
,
763 /* The PDE is valid, so read the PTE */
764 TempPte
= *PointerPte
;
765 if (TempPte
.u
.Hard
.Valid
== 1)
768 // Only two things can go wrong here:
769 // Executing NX page (we couldn't care less)
770 // Writing to a read-only page (the stuff ARM3 works with is write,
771 // so again, moot point).
775 // Otherwise, the PDE was probably invalid, and all is good now
777 return STATUS_SUCCESS
;
780 /* Get the current thread */
781 CurrentThread
= PsGetCurrentThread();
783 /* Check for a fault on the page table or hyperspace */
784 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
)) goto UserFault
;
786 /* Use the system working set */
787 WorkingSet
= &MmSystemCacheWs
;
788 CurrentProcess
= NULL
;
790 /* Acquire the working set lock */
791 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
792 MiLockWorkingSet(CurrentThread
, WorkingSet
);
794 /* Re-read PTE now that we own the lock */
795 TempPte
= *PointerPte
;
796 if (TempPte
.u
.Hard
.Valid
== 1)
798 // Only two things can go wrong here:
799 // Executing NX page (we couldn't care less)
800 // Writing to a read-only page (the stuff ARM3 works with is write,
801 // so again, moot point).
802 ASSERT(TempPte
.u
.Hard
.Write
== 1);
804 /* Release the working set */
805 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
806 KeLowerIrql(LockIrql
);
808 // Otherwise, the PDE was probably invalid, and all is good now
809 return STATUS_SUCCESS
;
812 /* Check one kind of prototype PTE */
813 if (TempPte
.u
.Soft
.Prototype
)
815 /* Make sure protected pool is on, and that this is a pool address */
816 if ((MmProtectFreedNonPagedPool
) &&
817 (((Address
>= MmNonPagedPoolStart
) &&
818 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
819 MmSizeOfNonPagedPoolInBytes
))) ||
820 ((Address
>= MmNonPagedPoolExpansionStart
) &&
821 (Address
< MmNonPagedPoolEnd
))))
823 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
824 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
831 /* Get the prototype PTE! */
832 ProtoPte
= MiProtoPteToPte(&TempPte
);
836 /* We don't implement transition PTEs */
837 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
839 /* Check for no-access PTE */
840 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
842 /* Bugcheck the system! */
843 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
846 (ULONG_PTR
)TrapInformation
,
850 /* Check for demand page */
851 if ((StoreInstruction
) && !(TempPte
.u
.Hard
.Valid
))
853 /* Get the protection code */
854 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
856 /* Bugcheck the system! */
857 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
860 (ULONG_PTR
)TrapInformation
,
866 /* Now do the real fault handling */
867 Status
= MiDispatchFault(StoreInstruction
,
876 /* Release the working set */
877 ASSERT(KeAreAllApcsDisabled() == TRUE
);
878 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
879 KeLowerIrql(LockIrql
);
882 DPRINT("Fault resolved with status: %lx\n", Status
);
886 /* This is a user fault */
888 CurrentThread
= PsGetCurrentThread();
889 CurrentProcess
= (PEPROCESS
)CurrentThread
->Tcb
.ApcState
.Process
;
891 /* Lock the working set */
892 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
894 #if (_MI_PAGING_LEVELS == 2)
895 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
898 #if (_MI_PAGING_LEVELS == 4)
899 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
900 // also this is missing the page count increment
901 /* Check if the PXE is valid */
902 if (PointerPxe
->u
.Hard
.Valid
== 0)
904 /* Right now, we only handle scenarios where the PXE is totally empty */
905 ASSERT(PointerPxe
->u
.Long
== 0);
907 /* Resolve a demand zero fault */
908 Status
= MiResolveDemandZeroFault(PointerPpe
,
913 /* We should come back with a valid PXE */
914 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
918 #if (_MI_PAGING_LEVELS >= 3)
919 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
920 // also this is missing the page count increment
921 /* Check if the PPE is valid */
922 if (PointerPpe
->u
.Hard
.Valid
== 0)
924 /* Right now, we only handle scenarios where the PPE is totally empty */
925 ASSERT(PointerPpe
->u
.Long
== 0);
927 /* Resolve a demand zero fault */
928 Status
= MiResolveDemandZeroFault(PointerPde
,
933 /* We should come back with a valid PPE */
934 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
938 /* Check if the PDE is valid */
939 if (PointerPde
->u
.Hard
.Valid
== 0)
941 /* Right now, we only handle scenarios where the PDE is totally empty */
942 ASSERT(PointerPde
->u
.Long
== 0);
944 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
948 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
949 if (ProtectionCode
== MM_NOACCESS
)
951 #if (_MI_PAGING_LEVELS == 2)
952 /* Could be a page table for paged pool */
953 MiCheckPdeForPagedPool(Address
);
955 /* Has the code above changed anything -- is this now a valid PTE? */
956 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
958 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
959 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
963 /* Write a demand-zero PDE */
964 MI_WRITE_INVALID_PTE(PointerPde
, DemandZeroPde
);
966 /* Dispatch the fault */
967 Status
= MiDispatchFault(TRUE
,
972 PsGetCurrentProcess(),
976 UserPdeFault
= FALSE
;
978 /* We should come back with APCs enabled, and with a valid PDE */
979 ASSERT(KeAreAllApcsDisabled() == TRUE
);
980 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
983 /* Now capture the PTE. Ignore virtual faults for now */
984 TempPte
= *PointerPte
;
985 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
987 /* Quick check for demand-zero */
988 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
990 /* Resolve the fault */
991 MiResolveDemandZeroFault(Address
,
996 /* Return the status */
997 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
998 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
1001 /* Make sure it's not a prototype PTE */
1002 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1004 /* Check if this address range belongs to a valid allocation (VAD) */
1005 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
1006 if (ProtectionCode
== MM_NOACCESS
)
1008 #if (_MI_PAGING_LEVELS == 2)
1009 /* Could be a page table for paged pool */
1010 MiCheckPdeForPagedPool(Address
);
1012 /* Has the code above changed anything -- is this now a valid PTE? */
1013 Status
= (PointerPte
->u
.Hard
.Valid
== 1) ? STATUS_SUCCESS
: STATUS_ACCESS_VIOLATION
;
1015 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1016 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1020 /* Check for non-demand zero PTE */
1021 if (TempPte
.u
.Long
!= 0)
1023 /* This is a page fault */
1025 /* FIXME: Run MiAccessCheck */
1027 /* Dispatch the fault */
1028 Status
= MiDispatchFault(StoreInstruction
,
1033 PsGetCurrentProcess(),
1037 /* Return the status */
1038 ASSERT(NT_SUCCESS(Status
));
1039 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1040 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1045 * Check if this is a real user-mode address or actually a kernel-mode
1046 * page table for a user mode address
1048 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
1050 /* Add an additional page table reference */
1051 MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
1052 ASSERT(MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
1055 /* No guard page support yet */
1056 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1058 /* Did we get a prototype PTE back? */
1061 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1062 if (PointerPde
== MiAddressToPde(PTE_BASE
))
1064 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1065 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPde
);
1069 /* No, create a new PTE. First, write the protection */
1070 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
1073 /* Lock the PFN database since we're going to grab a page */
1074 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1076 /* Try to get a zero page */
1077 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1078 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1079 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1080 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1081 if (!PageFrameIndex
)
1083 /* Grab a page out of there. Later we should grab a colored zero page */
1084 PageFrameIndex
= MiRemoveAnyPage(Color
);
1085 ASSERT(PageFrameIndex
);
1087 /* Release the lock since we need to do some zeroing */
1088 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1090 /* Zero out the page, since it's for user-mode */
1091 MiZeroPfn(PageFrameIndex
);
1093 /* Grab the lock again so we can initialize the PFN entry */
1094 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1097 /* Initialize the PFN entry now */
1098 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1100 /* One more demand-zero fault */
1101 KeGetCurrentPrcb()->MmDemandZeroCount
++;
1103 /* And we're done with the lock */
1104 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1106 /* Fault on user PDE, or fault on user PTE? */
1107 if (PointerPte
<= MiHighestUserPte
)
1109 /* User fault, build a user PTE */
1110 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1112 PointerPte
->u
.Soft
.Protection
,
1117 /* This is a user-mode PDE, create a kernel PTE for it */
1118 MI_MAKE_HARDWARE_PTE(&TempPte
,
1120 PointerPte
->u
.Soft
.Protection
,
1124 /* Write the dirty bit for writeable pages */
1125 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1127 /* And now write down the PTE, making the address valid */
1128 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1129 ASSERT(MiGetPfnEntry(PageFrameIndex
)->u1
.Event
== NULL
);
1132 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
1136 /* No guard page support yet */
1137 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1138 ASSERT(ProtectionCode
!= 0x100);
1140 /* Write the prototype PTE */
1141 TempPte
= PrototypePte
;
1142 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1143 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1145 /* Handle the fault */
1146 Status
= MiDispatchFault(StoreInstruction
,
1154 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
1155 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1156 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
!= 0);
1159 /* Release the working set */
1160 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
1166 MmGetExecuteOptions(IN PULONG ExecuteOptions
)
1168 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1169 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1171 *ExecuteOptions
= 0;
1173 if (CurrentProcess
->Flags
.ExecuteDisable
)
1175 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE
;
1178 if (CurrentProcess
->Flags
.ExecuteEnable
)
1180 *ExecuteOptions
|= MEM_EXECUTE_OPTION_ENABLE
;
1183 if (CurrentProcess
->Flags
.DisableThunkEmulation
)
1185 *ExecuteOptions
|= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
;
1188 if (CurrentProcess
->Flags
.Permanent
)
1190 *ExecuteOptions
|= MEM_EXECUTE_OPTION_PERMANENT
;
1193 if (CurrentProcess
->Flags
.ExecuteDispatchEnable
)
1195 *ExecuteOptions
|= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
;
1198 if (CurrentProcess
->Flags
.ImageDispatchEnable
)
1200 *ExecuteOptions
|= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
;
1203 return STATUS_SUCCESS
;
1208 MmSetExecuteOptions(IN ULONG ExecuteOptions
)
1210 PKPROCESS CurrentProcess
= &PsGetCurrentProcess()->Pcb
;
1211 KLOCK_QUEUE_HANDLE ProcessLock
;
1212 NTSTATUS Status
= STATUS_ACCESS_DENIED
;
1213 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1215 /* Only accept valid flags */
1216 if (ExecuteOptions
& ~MEM_EXECUTE_OPTION_VALID_FLAGS
)
1219 DPRINT1("Invalid no-execute options\n");
1220 return STATUS_INVALID_PARAMETER
;
1223 /* Change the NX state in the process lock */
1224 KiAcquireProcessLock(CurrentProcess
, &ProcessLock
);
1226 /* Don't change anything if the permanent flag was set */
1227 if (!CurrentProcess
->Flags
.Permanent
)
1229 /* Start by assuming it's not disabled */
1230 CurrentProcess
->Flags
.ExecuteDisable
= FALSE
;
1232 /* Now process each flag and turn the equivalent bit on */
1233 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE
)
1235 CurrentProcess
->Flags
.ExecuteDisable
= TRUE
;
1237 if (ExecuteOptions
& MEM_EXECUTE_OPTION_ENABLE
)
1239 CurrentProcess
->Flags
.ExecuteEnable
= TRUE
;
1241 if (ExecuteOptions
& MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
)
1243 CurrentProcess
->Flags
.DisableThunkEmulation
= TRUE
;
1245 if (ExecuteOptions
& MEM_EXECUTE_OPTION_PERMANENT
)
1247 CurrentProcess
->Flags
.Permanent
= TRUE
;
1249 if (ExecuteOptions
& MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE
)
1251 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1253 if (ExecuteOptions
& MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE
)
1255 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1258 /* These are turned on by default if no-execution is also eanbled */
1259 if (CurrentProcess
->Flags
.ExecuteEnable
)
1261 CurrentProcess
->Flags
.ExecuteDispatchEnable
= TRUE
;
1262 CurrentProcess
->Flags
.ImageDispatchEnable
= TRUE
;
1266 Status
= STATUS_SUCCESS
;
1269 /* Release the lock and return status */
1270 KiReleaseProcessLock(&ProcessLock
);