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 while (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 /* Check if the PDE is invalid */
723 if (PointerPde
->u
.Hard
.Valid
== 0)
725 #if (_MI_PAGING_LEVELS == 2)
726 /* Sync this PDE and check, if that made it valid */
727 if (!MiSynchronizeSystemPde(PointerPde
))
730 /* PDE (still) not valid, kill the system */
731 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
734 (ULONG_PTR
)TrapInformation
,
739 /* The PDE is valid, so read the PTE */
740 TempPte
= *PointerPte
;
741 if (TempPte
.u
.Hard
.Valid
== 1)
744 // Only two things can go wrong here:
745 // Executing NX page (we couldn't care less)
746 // Writing to a read-only page (the stuff ARM3 works with is write,
747 // so again, moot point).
751 // Otherwise, the PDE was probably invalid, and all is good now
753 return STATUS_SUCCESS
;
756 // Check for a fault on the page table or hyperspace
757 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address
))
759 #if (_MI_PAGING_LEVELS == 2)
760 /* Could be paged pool access from a new process -- synchronize the page directories */
761 if (MiCheckPdeForPagedPool(Address
) == STATUS_WAIT_1
)
763 DPRINT1("PAGE TABLES FAULTED IN!\n");
764 return STATUS_SUCCESS
;
767 /* Otherwise this could be a commit of a virtual address */
771 /* In this path, we are using the system working set */
772 CurrentThread
= PsGetCurrentThread();
773 WorkingSet
= &MmSystemCacheWs
;
776 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
777 MiLockWorkingSet(CurrentThread
, WorkingSet
);
779 /* Re-read PTE now that we own the lock */
780 TempPte
= *PointerPte
;
781 if (TempPte
.u
.Hard
.Valid
== 1)
783 // Only two things can go wrong here:
784 // Executing NX page (we couldn't care less)
785 // Writing to a read-only page (the stuff ARM3 works with is write,
786 // so again, moot point).
787 ASSERT(TempPte
.u
.Hard
.Write
== 1);
789 /* Release the working set */
790 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
791 KeLowerIrql(LockIrql
);
793 // Otherwise, the PDE was probably invalid, and all is good now
794 return STATUS_SUCCESS
;
797 /* Check one kind of prototype PTE */
798 if (TempPte
.u
.Soft
.Prototype
)
800 /* Make sure protected pool is on, and that this is a pool address */
801 if ((MmProtectFreedNonPagedPool
) &&
802 (((Address
>= MmNonPagedPoolStart
) &&
803 (Address
< (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
804 MmSizeOfNonPagedPoolInBytes
))) ||
805 ((Address
>= MmNonPagedPoolExpansionStart
) &&
806 (Address
< MmNonPagedPoolEnd
))))
808 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
809 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL
,
816 /* Get the prototype PTE! */
817 ProtoPte
= MiProtoPteToPte(&TempPte
);
821 /* We don't implement transition PTEs */
822 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
824 /* Check for no-access PTE */
825 if (TempPte
.u
.Soft
.Protection
== MM_NOACCESS
)
827 /* Bugcheck the system! */
828 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA
,
831 (ULONG_PTR
)TrapInformation
,
835 /* Check for demand page */
836 if ((StoreInstruction
) && !(TempPte
.u
.Hard
.Valid
))
838 /* Get the protection code */
839 if (!(TempPte
.u
.Soft
.Protection
& MM_READWRITE
))
841 /* Bugcheck the system! */
842 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY
,
845 (ULONG_PTR
)TrapInformation
,
851 /* Now do the real fault handling */
852 Status
= MiDispatchFault(StoreInstruction
,
861 /* Release the working set */
862 ASSERT(KeAreAllApcsDisabled() == TRUE
);
863 MiUnlockWorkingSet(CurrentThread
, WorkingSet
);
864 KeLowerIrql(LockIrql
);
867 DPRINT("Fault resolved with status: %lx\n", Status
);
871 #if (_MI_PAGING_LEVELS == 4)
872 /* On these systems we have PXEs and PPEs ready for everything we need */
873 if (PointerPxe
->u
.Hard
.Valid
== 0) return STATUS_ACCESS_VIOLATION
;
875 #if (_MI_PAGING_LEVELS >= 3)
876 if (PointerPpe
->u
.Hard
.Valid
== 0) return STATUS_ACCESS_VIOLATION
;
879 /* This is a user fault (<- And this is a lie!) */
880 CurrentThread
= PsGetCurrentThread();
881 CurrentProcess
= PsGetCurrentProcess();
883 /* Lock the working set */
884 MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
886 /* First things first, is the PDE valid? */
887 ASSERT(PointerPde
->u
.Hard
.LargePage
== 0);
888 if (PointerPde
->u
.Hard
.Valid
== 0)
890 /* Right now, we only handle scenarios where the PDE is totally empty */
891 ASSERT(PointerPde
->u
.Long
== 0);
893 /* Check if this address range belongs to a valid allocation (VAD) */
894 MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
896 /* Right now, we expect a valid protection mask on the VAD */
897 ASSERT(ProtectionCode
!= MM_NOACCESS
);
899 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
903 /* Resolve a demand zero fault */
904 Status
= MiResolveDemandZeroFault(PointerPte
,
909 UserPdeFault
= FALSE
;
911 /* We should come back with APCs enabled, and with a valid PDE */
912 ASSERT(KeAreAllApcsDisabled() == TRUE
);
913 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
916 /* Now capture the PTE. Ignore virtual faults for now */
917 TempPte
= *PointerPte
;
918 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
920 /* Quick check for demand-zero */
921 if (TempPte
.u
.Long
== (MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
))
923 /* Resolve the fault */
924 MiResolveDemandZeroFault(Address
,
925 (ULONG
)PointerPte
->u
.Soft
.Protection
,
929 /* Return the status */
930 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
931 return STATUS_PAGE_FAULT_DEMAND_ZERO
;
934 /* Make sure it's not a prototype PTE */
935 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
937 /* Check for non-demand zero PTE */
938 if (TempPte
.u
.Long
!= 0)
940 /* This is a page fault, check for valid protection */
941 ASSERT(TempPte
.u
.Soft
.Protection
!= 0x100);
943 /* FIXME: Run MiAccessCheck */
945 /* Dispatch the fault */
946 Status
= MiDispatchFault(StoreInstruction
,
951 PsGetCurrentProcess(),
955 /* Return the status */
956 ASSERT(NT_SUCCESS(Status
));
957 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
958 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
962 /* Check if this address range belongs to a valid allocation (VAD) */
963 ASSERT(TempPte
.u
.Long
== 0);
964 ProtoPte
= MiCheckVirtualAddress(Address
, &ProtectionCode
, &Vad
);
965 if (ProtectionCode
== MM_NOACCESS
)
967 /* This is a bogus VA */
968 Status
= STATUS_ACCESS_VIOLATION
;
970 /* Could be a not-yet-mapped paged pool page table */
971 #if (_MI_PAGING_LEVELS == 2)
972 MiCheckPdeForPagedPool(Address
);
974 /* See if that fixed it */
975 if (PointerPte
->u
.Hard
.Valid
== 1) Status
= STATUS_SUCCESS
;
977 /* Return the status */
978 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
982 /* Is this a user address? */
983 if (Address
<= MM_HIGHEST_USER_ADDRESS
)
985 /* Add an additional page table reference */
986 MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)]++;
987 ASSERT(MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Address
)] <= PTE_COUNT
);
990 /* Did we get a prototype PTE back? */
993 /* No, create a new PTE. First, write the protection */
994 PointerPte
->u
.Soft
.Protection
= ProtectionCode
;
996 /* Lock the PFN database since we're going to grab a page */
997 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
999 /* Try to get a zero page */
1000 MI_SET_USAGE(MI_USAGE_PEB_TEB
);
1001 MI_SET_PROCESS2(CurrentProcess
->ImageFileName
);
1002 Color
= MI_GET_NEXT_PROCESS_COLOR(CurrentProcess
);
1003 PageFrameIndex
= MiRemoveZeroPageSafe(Color
);
1004 if (!PageFrameIndex
)
1006 /* Grab a page out of there. Later we should grab a colored zero page */
1007 PageFrameIndex
= MiRemoveAnyPage(Color
);
1008 ASSERT(PageFrameIndex
);
1010 /* Release the lock since we need to do some zeroing */
1011 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1013 /* Zero out the page, since it's for user-mode */
1014 MiZeroPfn(PageFrameIndex
);
1016 /* Grab the lock again so we can initialize the PFN entry */
1017 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1020 /* Initialize the PFN entry now */
1021 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
1023 /* One more demand-zero fault */
1024 KeGetCurrentPrcb()->MmDemandZeroCount
++;
1026 /* And we're done with the lock */
1027 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1030 /* Was the fault on an actual user page, or a kernel page for the user? */
1031 if (PointerPte
<= MiHighestUserPte
)
1033 /* User fault, build a user PTE */
1034 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1036 PointerPte
->u
.Soft
.Protection
,
1041 /* Session, kernel, or user PTE, figure it out and build it */
1042 MI_MAKE_HARDWARE_PTE(&TempPte
,
1044 PointerPte
->u
.Soft
.Protection
,
1048 /* Write the dirty bit for writeable pages */
1049 if (MI_IS_PAGE_WRITEABLE(&TempPte
)) MI_MAKE_DIRTY_PAGE(&TempPte
);
1051 /* And now write down the PTE, making the address valid */
1052 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1055 Status
= STATUS_PAGE_FAULT_DEMAND_ZERO
;
1059 /* No guard page support yet */
1060 ASSERT((ProtectionCode
& MM_DECOMMIT
) == 0);
1061 ASSERT(ProtectionCode
!= 0x100);
1063 /* Write the prototype PTE */
1064 TempPte
= PrototypePte
;
1065 TempPte
.u
.Soft
.Protection
= ProtectionCode
;
1066 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1068 /* Handle the fault */
1069 Status
= MiDispatchFault(StoreInstruction
,
1077 ASSERT(Status
== STATUS_PAGE_FAULT_TRANSITION
);
1078 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1079 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
!= 0);
1082 /* Release the working set */
1083 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);