xref: /reactos/ntoskrnl/mm/ARM3/pagfault.c (revision afc27ab1)
1 /*
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
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
17 
18 /* GLOBALS ********************************************************************/
19 
20 #define HYDRA_PROCESS (PEPROCESS)1
21 #if MI_TRACE_PFNS
22 BOOLEAN UserPdeFault = FALSE;
23 #endif
24 
25 /* PRIVATE FUNCTIONS **********************************************************/
26 
27 static
28 NTSTATUS
29 NTAPI
30 MiCheckForUserStackOverflow(IN PVOID Address,
31                             IN PVOID TrapInformation)
32 {
33     PETHREAD CurrentThread = PsGetCurrentThread();
34     PTEB Teb = CurrentThread->Tcb.Teb;
35     PVOID StackBase, DeallocationStack, NextStackAddress;
36     SIZE_T GuaranteedSize;
37     NTSTATUS Status;
38 
39     /* Do we own the address space lock? */
40     if (CurrentThread->AddressSpaceOwner == 1)
41     {
42         /* This isn't valid */
43         DPRINT1("Process owns address space lock\n");
44         ASSERT(KeAreAllApcsDisabled() == TRUE);
45         return STATUS_GUARD_PAGE_VIOLATION;
46     }
47 
48     /* Are we attached? */
49     if (KeIsAttachedProcess())
50     {
51         /* This isn't valid */
52         DPRINT1("Process is attached\n");
53         return STATUS_GUARD_PAGE_VIOLATION;
54     }
55 
56     /* Read the current settings */
57     StackBase = Teb->NtTib.StackBase;
58     DeallocationStack = Teb->DeallocationStack;
59     GuaranteedSize = Teb->GuaranteedStackBytes;
60     DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
61             StackBase, DeallocationStack, GuaranteedSize);
62 
63     /* Guarantees make this code harder, for now, assume there aren't any */
64     ASSERT(GuaranteedSize == 0);
65 
66     /* So allocate only the minimum guard page size */
67     GuaranteedSize = PAGE_SIZE;
68 
69     /* Does this faulting stack address actually exist in the stack? */
70     if ((Address >= StackBase) || (Address < DeallocationStack))
71     {
72         /* That's odd... */
73         DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
74                 Address, StackBase, DeallocationStack);
75         return STATUS_GUARD_PAGE_VIOLATION;
76     }
77 
78     /* This is where the stack will start now */
79     NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(Address) - GuaranteedSize);
80 
81     /* Do we have at least one page between here and the end of the stack? */
82     if (((ULONG_PTR)NextStackAddress - PAGE_SIZE) <= (ULONG_PTR)DeallocationStack)
83     {
84         /* We don't -- Trying to make this guard page valid now */
85         DPRINT1("Close to our death...\n");
86 
87         /* Calculate the next memory address */
88         NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(DeallocationStack) + GuaranteedSize);
89 
90         /* Allocate the memory */
91         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
92                                          &NextStackAddress,
93                                          0,
94                                          &GuaranteedSize,
95                                          MEM_COMMIT,
96                                          PAGE_READWRITE);
97         if (NT_SUCCESS(Status))
98         {
99             /* Success! */
100             Teb->NtTib.StackLimit = NextStackAddress;
101         }
102         else
103         {
104             DPRINT1("Failed to allocate memory\n");
105         }
106 
107         return STATUS_STACK_OVERFLOW;
108     }
109 
110     /* Don't handle this flag yet */
111     ASSERT((PsGetCurrentProcess()->Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) == 0);
112 
113     /* Update the stack limit */
114     Teb->NtTib.StackLimit = (PVOID)((ULONG_PTR)NextStackAddress + GuaranteedSize);
115 
116     /* Now move the guard page to the next page */
117     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
118                                      &NextStackAddress,
119                                      0,
120                                      &GuaranteedSize,
121                                      MEM_COMMIT,
122                                      PAGE_READWRITE | PAGE_GUARD);
123     if ((NT_SUCCESS(Status) || (Status == STATUS_ALREADY_COMMITTED)))
124     {
125         /* We did it! */
126         DPRINT("Guard page handled successfully for %p\n", Address);
127         return STATUS_PAGE_FAULT_GUARD_PAGE;
128     }
129 
130     /* Fail, we couldn't move the guard page */
131     DPRINT1("Guard page failure: %lx\n", Status);
132     ASSERT(FALSE);
133     return STATUS_STACK_OVERFLOW;
134 }
135 
136 FORCEINLINE
137 BOOLEAN
138 MiIsAccessAllowed(
139     _In_ ULONG ProtectionMask,
140     _In_ BOOLEAN Write,
141     _In_ BOOLEAN Execute)
142 {
143     #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
144         (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
145         ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
146     static const UCHAR AccessAllowedMask[2][2] =
147     {
148         {   // Protect 0  1  2  3  4  5  6  7
149             _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
150             _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
151         },
152         {
153             _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
154             _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
155         }
156     };
157 
158     /* We want only the lower access bits */
159     ProtectionMask &= MM_PROTECT_ACCESS;
160 
161     /* Look it up in the table */
162     return (AccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1;
163 }
164 
165 static
166 NTSTATUS
167 NTAPI
168 MiAccessCheck(IN PMMPTE PointerPte,
169               IN BOOLEAN StoreInstruction,
170               IN KPROCESSOR_MODE PreviousMode,
171               IN ULONG_PTR ProtectionMask,
172               IN PVOID TrapFrame,
173               IN BOOLEAN LockHeld)
174 {
175     MMPTE TempPte;
176 
177     /* Check for invalid user-mode access */
178     if ((PreviousMode == UserMode) && (PointerPte > MiHighestUserPte))
179     {
180         return STATUS_ACCESS_VIOLATION;
181     }
182 
183     /* Capture the PTE -- is it valid? */
184     TempPte = *PointerPte;
185     if (TempPte.u.Hard.Valid)
186     {
187         /* Was someone trying to write to it? */
188         if (StoreInstruction)
189         {
190             /* Is it writable?*/
191             if (MI_IS_PAGE_WRITEABLE(&TempPte) ||
192                 MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
193             {
194                 /* Then there's nothing to worry about */
195                 return STATUS_SUCCESS;
196             }
197 
198             /* Oops! This isn't allowed */
199             return STATUS_ACCESS_VIOLATION;
200         }
201 
202         /* Someone was trying to read from a valid PTE, that's fine too */
203         return STATUS_SUCCESS;
204     }
205 
206     /* Check if the protection on the page allows what is being attempted */
207     if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE))
208     {
209         return STATUS_ACCESS_VIOLATION;
210     }
211 
212     /* Check if this is a guard page */
213     if ((ProtectionMask & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
214     {
215         ASSERT(ProtectionMask != MM_DECOMMIT);
216 
217         /* Attached processes can't expand their stack */
218         if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION;
219 
220         /* No support for prototype PTEs yet */
221         ASSERT(TempPte.u.Soft.Prototype == 0);
222 
223         /* Remove the guard page bit, and return a guard page violation */
224         TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE;
225         ASSERT(TempPte.u.Long != 0);
226         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
227         return STATUS_GUARD_PAGE_VIOLATION;
228     }
229 
230     /* Nothing to do */
231     return STATUS_SUCCESS;
232 }
233 
234 static
235 PMMPTE
236 NTAPI
237 MiCheckVirtualAddress(IN PVOID VirtualAddress,
238                       OUT PULONG ProtectCode,
239                       OUT PMMVAD *ProtoVad)
240 {
241     PMMVAD Vad;
242     PMMPTE PointerPte;
243 
244     /* No prototype/section support for now */
245     *ProtoVad = NULL;
246 
247     /* User or kernel fault? */
248     if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS)
249     {
250         /* Special case for shared data */
251         if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
252         {
253             /* It's a read-only page */
254             *ProtectCode = MM_READONLY;
255             return MmSharedUserDataPte;
256         }
257 
258         /* Find the VAD, it might not exist if the address is bogus */
259         Vad = MiLocateAddress(VirtualAddress);
260         if (!Vad)
261         {
262             /* Bogus virtual address */
263             *ProtectCode = MM_NOACCESS;
264             return NULL;
265         }
266 
267         /* ReactOS does not handle physical memory VADs yet */
268         ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
269 
270         /* Check if it's a section, or just an allocation */
271         if (Vad->u.VadFlags.PrivateMemory)
272         {
273             /* ReactOS does not handle AWE VADs yet */
274             ASSERT(Vad->u.VadFlags.VadType != VadAwe);
275 
276             /* This must be a TEB/PEB VAD */
277             if (Vad->u.VadFlags.MemCommit)
278             {
279                 /* It's committed, so return the VAD protection */
280                 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
281             }
282             else
283             {
284                 /* It has not yet been committed, so return no access */
285                 *ProtectCode = MM_NOACCESS;
286             }
287 
288             /* In both cases, return no PTE */
289             return NULL;
290         }
291         else
292         {
293             /* ReactOS does not supoprt these VADs yet */
294             ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
295             ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
296 
297             /* Return the proto VAD */
298             *ProtoVad = Vad;
299 
300             /* Get the prototype PTE for this page */
301             PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
302             ASSERT(PointerPte != NULL);
303             ASSERT(PointerPte <= Vad->LastContiguousPte);
304 
305             /* Return the Prototype PTE and the protection for the page mapping */
306             *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
307             return PointerPte;
308         }
309     }
310     else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
311     {
312         /* This should never happen, as these addresses are handled by the double-maping */
313         if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
314             ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
315         {
316             /* Fail such access */
317             *ProtectCode = MM_NOACCESS;
318             return NULL;
319         }
320 
321         /* Return full access rights */
322         *ProtectCode = MM_EXECUTE_READWRITE;
323         return NULL;
324     }
325     else if (MI_IS_SESSION_ADDRESS(VirtualAddress))
326     {
327         /* ReactOS does not have an image list yet, so bail out to failure case */
328         ASSERT(IsListEmpty(&MmSessionSpace->ImageList));
329     }
330 
331     /* Default case -- failure */
332     *ProtectCode = MM_NOACCESS;
333     return NULL;
334 }
335 
336 #if (_MI_PAGING_LEVELS == 2)
337 static
338 NTSTATUS
339 FASTCALL
340 MiCheckPdeForSessionSpace(IN PVOID Address)
341 {
342     MMPTE TempPde;
343     PMMPDE PointerPde;
344     PVOID SessionAddress;
345     ULONG Index;
346 
347     /* Is this a session PTE? */
348     if (MI_IS_SESSION_PTE(Address))
349     {
350         /* Make sure the PDE for session space is valid */
351         PointerPde = MiAddressToPde(MmSessionSpace);
352         if (!PointerPde->u.Hard.Valid)
353         {
354             /* This means there's no valid session, bail out */
355             DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
356                      Address);
357             DbgBreakPoint();
358             return STATUS_ACCESS_VIOLATION;
359         }
360 
361         /* Now get the session-specific page table for this address */
362         SessionAddress = MiPteToAddress(Address);
363         PointerPde = MiAddressToPte(Address);
364         if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
365 
366         /* It's not valid, so find it in the page table array */
367         Index = ((ULONG_PTR)SessionAddress - (ULONG_PTR)MmSessionBase) >> 22;
368         TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
369         if (TempPde.u.Hard.Valid)
370         {
371             /* The copy is valid, so swap it in */
372             InterlockedExchange((PLONG)PointerPde, TempPde.u.Long);
373             return STATUS_WAIT_1;
374         }
375 
376         /* We don't seem to have allocated a page table for this address yet? */
377         DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
378                  PointerPde->u.Long, SessionAddress);
379         DbgBreakPoint();
380         return STATUS_ACCESS_VIOLATION;
381     }
382 
383     /* Is the address also a session address? If not, we're done */
384     if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS;
385 
386     /* It is, so again get the PDE for session space */
387     PointerPde = MiAddressToPde(MmSessionSpace);
388     if (!PointerPde->u.Hard.Valid)
389     {
390         /* This means there's no valid session, bail out */
391         DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
392                     Address);
393         DbgBreakPoint();
394         return STATUS_ACCESS_VIOLATION;
395     }
396 
397     /* Now get the PDE for the address itself */
398     PointerPde = MiAddressToPde(Address);
399     if (!PointerPde->u.Hard.Valid)
400     {
401         /* Do the swap, we should be good to go */
402         Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22;
403         PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
404         if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
405 
406         /* We had not allocated a page table for this session address yet, fail! */
407         DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
408                  PointerPde->u.Long, Address);
409         DbgBreakPoint();
410         return STATUS_ACCESS_VIOLATION;
411     }
412 
413     /* It's valid, so there's nothing to do */
414     return STATUS_SUCCESS;
415 }
416 
417 NTSTATUS
418 FASTCALL
419 MiCheckPdeForPagedPool(IN PVOID Address)
420 {
421     PMMPDE PointerPde;
422     NTSTATUS Status = STATUS_SUCCESS;
423 
424     /* Check session PDE */
425     if (MI_IS_SESSION_ADDRESS(Address)) return MiCheckPdeForSessionSpace(Address);
426     if (MI_IS_SESSION_PTE(Address)) return MiCheckPdeForSessionSpace(Address);
427 
428     //
429     // Check if this is a fault while trying to access the page table itself
430     //
431     if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
432     {
433         //
434         // Send a hint to the page fault handler that this is only a valid fault
435         // if we already detected this was access within the page table range
436         //
437         PointerPde = (PMMPDE)MiAddressToPte(Address);
438         Status = STATUS_WAIT_1;
439     }
440     else if (Address < MmSystemRangeStart)
441     {
442         //
443         // This is totally illegal
444         //
445         return STATUS_ACCESS_VIOLATION;
446     }
447     else
448     {
449         //
450         // Get the PDE for the address
451         //
452         PointerPde = MiAddressToPde(Address);
453     }
454 
455     //
456     // Check if it's not valid
457     //
458     if (PointerPde->u.Hard.Valid == 0)
459     {
460         //
461         // Copy it from our double-mapped system page directory
462         //
463         InterlockedExchangePte(PointerPde,
464                                MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
465     }
466 
467     //
468     // Return status
469     //
470     return Status;
471 }
472 #else
473 NTSTATUS
474 FASTCALL
475 MiCheckPdeForPagedPool(IN PVOID Address)
476 {
477     return STATUS_ACCESS_VIOLATION;
478 }
479 #endif
480 
481 VOID
482 NTAPI
483 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
484 {
485     PMMPTE ZeroPte;
486     MMPTE TempPte;
487     PMMPFN Pfn1;
488     PVOID ZeroAddress;
489 
490     /* Get the PFN for this page */
491     Pfn1 = MiGetPfnEntry(PageFrameNumber);
492     ASSERT(Pfn1);
493 
494     /* Grab a system PTE we can use to zero the page */
495     ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
496     ASSERT(ZeroPte);
497 
498     /* Initialize the PTE for it */
499     TempPte = ValidKernelPte;
500     TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
501 
502     /* Setup caching */
503     if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
504     {
505         /* Write combining, no caching */
506         MI_PAGE_DISABLE_CACHE(&TempPte);
507         MI_PAGE_WRITE_COMBINED(&TempPte);
508     }
509     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
510     {
511         /* Write through, no caching */
512         MI_PAGE_DISABLE_CACHE(&TempPte);
513         MI_PAGE_WRITE_THROUGH(&TempPte);
514     }
515 
516     /* Make the system PTE valid with our PFN */
517     MI_WRITE_VALID_PTE(ZeroPte, TempPte);
518 
519     /* Get the address it maps to, and zero it out */
520     ZeroAddress = MiPteToAddress(ZeroPte);
521     KeZeroPages(ZeroAddress, PAGE_SIZE);
522 
523     /* Now get rid of it */
524     MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
525 }
526 
527 VOID
528 NTAPI
529 MiCopyPfn(
530     _In_ PFN_NUMBER DestPage,
531     _In_ PFN_NUMBER SrcPage)
532 {
533     PMMPTE SysPtes;
534     MMPTE TempPte;
535     PMMPFN DestPfn, SrcPfn;
536     PVOID DestAddress;
537     const VOID* SrcAddress;
538 
539     /* Get the PFNs */
540     DestPfn = MiGetPfnEntry(DestPage);
541     ASSERT(DestPfn);
542     SrcPfn = MiGetPfnEntry(SrcPage);
543     ASSERT(SrcPfn);
544 
545     /* Grab 2 system PTEs */
546     SysPtes = MiReserveSystemPtes(2, SystemPteSpace);
547     ASSERT(SysPtes);
548 
549     /* Initialize the destination PTE */
550     TempPte = ValidKernelPte;
551     TempPte.u.Hard.PageFrameNumber = DestPage;
552 
553     /* Setup caching */
554     if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined)
555     {
556         /* Write combining, no caching */
557         MI_PAGE_DISABLE_CACHE(&TempPte);
558         MI_PAGE_WRITE_COMBINED(&TempPte);
559     }
560     else if (DestPfn->u3.e1.CacheAttribute == MiNonCached)
561     {
562         /* Write through, no caching */
563         MI_PAGE_DISABLE_CACHE(&TempPte);
564         MI_PAGE_WRITE_THROUGH(&TempPte);
565     }
566 
567     /* Make the system PTE valid with our PFN */
568     MI_WRITE_VALID_PTE(&SysPtes[0], TempPte);
569 
570     /* Initialize the source PTE */
571     TempPte = ValidKernelPte;
572     TempPte.u.Hard.PageFrameNumber = SrcPage;
573 
574     /* Setup caching */
575     if (SrcPfn->u3.e1.CacheAttribute == MiNonCached)
576     {
577         MI_PAGE_DISABLE_CACHE(&TempPte);
578     }
579 
580     /* Make the system PTE valid with our PFN */
581     MI_WRITE_VALID_PTE(&SysPtes[1], TempPte);
582 
583     /* Get the addresses and perform the copy */
584     DestAddress = MiPteToAddress(&SysPtes[0]);
585     SrcAddress = MiPteToAddress(&SysPtes[1]);
586     RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
587 
588     /* Now get rid of it */
589     MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace);
590 }
591 
592 static
593 NTSTATUS
594 NTAPI
595 MiResolveDemandZeroFault(IN PVOID Address,
596                          IN PMMPTE PointerPte,
597                          IN ULONG Protection,
598                          IN PEPROCESS Process,
599                          IN KIRQL OldIrql)
600 {
601     PFN_NUMBER PageFrameNumber = 0;
602     MMPTE TempPte;
603     BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
604     ULONG Color;
605     PMMPFN Pfn1;
606     DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
607             Address,
608             Process);
609 
610     /* Must currently only be called by paging path */
611     if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL))
612     {
613         /* Sanity check */
614         ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
615 
616         /* No forking yet */
617         ASSERT(Process->ForkInProgress == NULL);
618 
619         /* Get process color */
620         Color = MI_GET_NEXT_PROCESS_COLOR(Process);
621         ASSERT(Color != 0xFFFFFFFF);
622 
623         /* We'll need a zero page */
624         NeedZero = TRUE;
625     }
626     else
627     {
628         /* Check if we need a zero page */
629         NeedZero = (OldIrql != MM_NOIRQL);
630 
631         /* Session-backed image views must be zeroed */
632         if ((Process == HYDRA_PROCESS) &&
633             ((MI_IS_SESSION_IMAGE_ADDRESS(Address)) ||
634              ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs))))
635         {
636             NeedZero = TRUE;
637         }
638 
639         /* Hardcode unknown color */
640         Color = 0xFFFFFFFF;
641     }
642 
643     /* Check if the PFN database should be acquired */
644     if (OldIrql == MM_NOIRQL)
645     {
646         /* Acquire it and remember we should release it after */
647         OldIrql = MiAcquirePfnLock();
648         HaveLock = TRUE;
649     }
650 
651     /* We either manually locked the PFN DB, or already came with it locked */
652     MI_ASSERT_PFN_LOCK_HELD();
653     ASSERT(PointerPte->u.Hard.Valid == 0);
654 
655     /* Assert we have enough pages */
656     ASSERT(MmAvailablePages >= 32);
657 
658 #if MI_TRACE_PFNS
659     if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
660     if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
661 #endif
662     if (Process == HYDRA_PROCESS) MI_SET_PROCESS2("Hydra");
663     else if (Process) MI_SET_PROCESS2(Process->ImageFileName);
664     else MI_SET_PROCESS2("Kernel Demand 0");
665 
666     /* Do we need a zero page? */
667     if (Color != 0xFFFFFFFF)
668     {
669         /* Try to get one, if we couldn't grab a free page and zero it */
670         PageFrameNumber = MiRemoveZeroPageSafe(Color);
671         if (!PageFrameNumber)
672         {
673             /* We'll need a free page and zero it manually */
674             PageFrameNumber = MiRemoveAnyPage(Color);
675             NeedZero = TRUE;
676         }
677     }
678     else
679     {
680         /* Get a color, and see if we should grab a zero or non-zero page */
681         Color = MI_GET_NEXT_COLOR();
682         if (!NeedZero)
683         {
684             /* Process or system doesn't want a zero page, grab anything */
685             PageFrameNumber = MiRemoveAnyPage(Color);
686         }
687         else
688         {
689             /* System wants a zero page, obtain one */
690             PageFrameNumber = MiRemoveZeroPage(Color);
691         }
692     }
693 
694     /* Initialize it */
695     MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
696 
697     /* Increment demand zero faults */
698     KeGetCurrentPrcb()->MmDemandZeroCount++;
699 
700     /* Do we have the lock? */
701     if (HaveLock)
702     {
703         /* Release it */
704         MiReleasePfnLock(OldIrql);
705 
706         /* Update performance counters */
707         if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
708     }
709 
710     /* Zero the page if need be */
711     if (NeedZero) MiZeroPfn(PageFrameNumber);
712 
713     /* Fault on user PDE, or fault on user PTE? */
714     if (PointerPte <= MiHighestUserPte)
715     {
716         /* User fault, build a user PTE */
717         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
718                                   PointerPte,
719                                   Protection,
720                                   PageFrameNumber);
721     }
722     else
723     {
724         /* This is a user-mode PDE, create a kernel PTE for it */
725         MI_MAKE_HARDWARE_PTE(&TempPte,
726                              PointerPte,
727                              Protection,
728                              PageFrameNumber);
729     }
730 
731     /* Set it dirty if it's a writable page */
732     if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
733 
734     /* Write it */
735     MI_WRITE_VALID_PTE(PointerPte, TempPte);
736 
737     /* Did we manually acquire the lock */
738     if (HaveLock)
739     {
740         /* Get the PFN entry */
741         Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
742 
743         /* Windows does these sanity checks */
744         ASSERT(Pfn1->u1.Event == 0);
745         ASSERT(Pfn1->u3.e1.PrototypePte == 0);
746     }
747 
748     //
749     // It's all good now
750     //
751     DPRINT("Demand zero page has now been paged in\n");
752     return STATUS_PAGE_FAULT_DEMAND_ZERO;
753 }
754 
755 static
756 NTSTATUS
757 NTAPI
758 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
759                         IN PVOID Address,
760                         IN PMMPTE PointerPte,
761                         IN PMMPTE PointerProtoPte,
762                         IN KIRQL OldIrql,
763                         IN PMMPFN* LockedProtoPfn)
764 {
765     MMPTE TempPte;
766     PMMPTE OriginalPte, PageTablePte;
767     ULONG_PTR Protection;
768     PFN_NUMBER PageFrameIndex;
769     PMMPFN Pfn1, Pfn2;
770     BOOLEAN OriginalProtection, DirtyPage;
771 
772     /* Must be called with an valid prototype PTE, with the PFN lock held */
773     MI_ASSERT_PFN_LOCK_HELD();
774     ASSERT(PointerProtoPte->u.Hard.Valid == 1);
775 
776     /* Get the page */
777     PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
778 
779     /* Get the PFN entry and set it as a prototype PTE */
780     Pfn1 = MiGetPfnEntry(PageFrameIndex);
781     Pfn1->u3.e1.PrototypePte = 1;
782 
783     /* Increment the share count for the page table */
784     PageTablePte = MiAddressToPte(PointerPte);
785     Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
786     Pfn2->u2.ShareCount++;
787 
788     /* Check where we should be getting the protection information from */
789     if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
790     {
791         /* Get the protection from the PTE, there's no real Proto PTE data */
792         Protection = PointerPte->u.Soft.Protection;
793 
794         /* Remember that we did not use the proto protection */
795         OriginalProtection = FALSE;
796     }
797     else
798     {
799         /* Get the protection from the original PTE link */
800         OriginalPte = &Pfn1->OriginalPte;
801         Protection = OriginalPte->u.Soft.Protection;
802 
803         /* Remember that we used the original protection */
804         OriginalProtection = TRUE;
805 
806         /* Check if this was a write on a read only proto */
807         if ((StoreInstruction) && !(Protection & MM_READWRITE))
808         {
809             /* Clear the flag */
810             StoreInstruction = 0;
811         }
812     }
813 
814     /* Check if this was a write on a non-COW page */
815     DirtyPage = FALSE;
816     if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
817     {
818         /* Then the page should be marked dirty */
819         DirtyPage = TRUE;
820 
821         /* ReactOS check */
822         ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
823     }
824 
825     /* Did we get a locked incoming PFN? */
826     if (*LockedProtoPfn)
827     {
828         /* Drop a reference */
829         ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
830         MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
831         *LockedProtoPfn = NULL;
832     }
833 
834     /* Release the PFN lock */
835     MiReleasePfnLock(OldIrql);
836 
837     /* Remove special/caching bits */
838     Protection &= ~MM_PROTECT_SPECIAL;
839 
840     /* Setup caching */
841     if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
842     {
843         /* Write combining, no caching */
844         MI_PAGE_DISABLE_CACHE(&TempPte);
845         MI_PAGE_WRITE_COMBINED(&TempPte);
846     }
847     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
848     {
849         /* Write through, no caching */
850         MI_PAGE_DISABLE_CACHE(&TempPte);
851         MI_PAGE_WRITE_THROUGH(&TempPte);
852     }
853 
854     /* Check if this is a kernel or user address */
855     if (Address < MmSystemRangeStart)
856     {
857         /* Build the user PTE */
858         MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
859     }
860     else
861     {
862         /* Build the kernel PTE */
863         MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
864     }
865 
866     /* Set the dirty flag if needed */
867     if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte);
868 
869     /* Write the PTE */
870     MI_WRITE_VALID_PTE(PointerPte, TempPte);
871 
872     /* Reset the protection if needed */
873     if (OriginalProtection) Protection = MM_ZERO_ACCESS;
874 
875     /* Return success */
876     ASSERT(PointerPte == MiAddressToPte(Address));
877     return STATUS_SUCCESS;
878 }
879 
880 static
881 NTSTATUS
882 NTAPI
883 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
884                        _In_ PVOID FaultingAddress,
885                        _In_ PMMPTE PointerPte,
886                        _In_ PEPROCESS CurrentProcess,
887                        _Inout_ KIRQL *OldIrql)
888 {
889     ULONG Color;
890     PFN_NUMBER Page;
891     NTSTATUS Status;
892     MMPTE TempPte = *PointerPte;
893     PMMPFN Pfn1;
894     ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
895     ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
896     ULONG Protection = TempPte.u.Soft.Protection;
897 
898     /* Things we don't support yet */
899     ASSERT(CurrentProcess > HYDRA_PROCESS);
900     ASSERT(*OldIrql != MM_NOIRQL);
901 
902     MI_SET_USAGE(MI_USAGE_PAGE_FILE);
903     MI_SET_PROCESS(CurrentProcess);
904 
905     /* We must hold the PFN lock */
906     MI_ASSERT_PFN_LOCK_HELD();
907 
908     /* Some sanity checks */
909     ASSERT(TempPte.u.Hard.Valid == 0);
910     ASSERT(TempPte.u.Soft.PageFileHigh != 0);
911     ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
912 
913     /* Get any page, it will be overwritten */
914     Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
915     Page = MiRemoveAnyPage(Color);
916 
917     /* Initialize this PFN */
918     MiInitializePfn(Page, PointerPte, StoreInstruction);
919 
920     /* Sets the PFN as being in IO operation */
921     Pfn1 = MI_PFN_ELEMENT(Page);
922     ASSERT(Pfn1->u1.Event == NULL);
923     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
924     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
925     Pfn1->u3.e1.ReadInProgress = 1;
926 
927     /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
928     MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection);
929 
930     MI_WRITE_INVALID_PTE(PointerPte, TempPte);
931 
932     /* Release the PFN lock while we proceed */
933     MiReleasePfnLock(*OldIrql);
934 
935     /* Do the paging IO */
936     Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset);
937 
938     /* Lock the PFN database again */
939     *OldIrql = MiAcquirePfnLock();
940 
941     /* Nobody should have changed that while we were not looking */
942     ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
943     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
944 
945     if (!NT_SUCCESS(Status))
946     {
947         /* Malheur! */
948         ASSERT(FALSE);
949         Pfn1->u4.InPageError = 1;
950         Pfn1->u1.ReadStatus = Status;
951     }
952 
953     /* And the PTE can finally be valid */
954     MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page);
955     MI_WRITE_VALID_PTE(PointerPte, TempPte);
956 
957     Pfn1->u3.e1.ReadInProgress = 0;
958     /* Did someone start to wait on us while we proceeded ? */
959     if (Pfn1->u1.Event)
960     {
961         /* Tell them we're done */
962         KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
963     }
964 
965     return Status;
966 }
967 
968 static
969 NTSTATUS
970 NTAPI
971 MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
972                          IN PVOID FaultingAddress,
973                          IN PMMPTE PointerPte,
974                          IN PEPROCESS CurrentProcess,
975                          IN KIRQL OldIrql,
976                          OUT PKEVENT **InPageBlock)
977 {
978     PFN_NUMBER PageFrameIndex;
979     PMMPFN Pfn1;
980     MMPTE TempPte;
981     PMMPTE PointerToPteForProtoPage;
982     DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
983             FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
984 
985     /* Windowss does this check */
986     ASSERT(*InPageBlock == NULL);
987 
988     /* ARM3 doesn't support this path */
989     ASSERT(OldIrql != MM_NOIRQL);
990 
991     /* Capture the PTE and make sure it's in transition format */
992     TempPte = *PointerPte;
993     ASSERT((TempPte.u.Soft.Valid == 0) &&
994            (TempPte.u.Soft.Prototype == 0) &&
995            (TempPte.u.Soft.Transition == 1));
996 
997     /* Get the PFN and the PFN entry */
998     PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
999     DPRINT("Transition PFN: %lx\n", PageFrameIndex);
1000     Pfn1 = MiGetPfnEntry(PageFrameIndex);
1001 
1002     /* One more transition fault! */
1003     InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
1004 
1005     /* This is from ARM3 -- Windows normally handles this here */
1006     ASSERT(Pfn1->u4.InPageError == 0);
1007 
1008     /* See if we should wait before terminating the fault */
1009     if ((Pfn1->u3.e1.ReadInProgress == 1)
1010             || ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction))
1011     {
1012         DPRINT1("The page is currently in a page transition !\n");
1013         *InPageBlock = &Pfn1->u1.Event;
1014         if (PointerPte == Pfn1->PteAddress)
1015         {
1016             DPRINT1("And this if for this particular PTE.\n");
1017             /* The PTE will be made valid by the thread serving the fault */
1018             return STATUS_SUCCESS; // FIXME: Maybe something more descriptive
1019         }
1020     }
1021 
1022     /* Windows checks there's some free pages and this isn't an in-page error */
1023     ASSERT(MmAvailablePages > 0);
1024     ASSERT(Pfn1->u4.InPageError == 0);
1025 
1026     /* ReactOS checks for this */
1027     ASSERT(MmAvailablePages > 32);
1028 
1029     /* Was this a transition page in the valid list, or free/zero list? */
1030     if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
1031     {
1032         /* All Windows does here is a bunch of sanity checks */
1033         DPRINT("Transition in active list\n");
1034         ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
1035                (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
1036         ASSERT(Pfn1->u2.ShareCount != 0);
1037         ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
1038     }
1039     else
1040     {
1041         /* Otherwise, the page is removed from its list */
1042         DPRINT("Transition page in free/zero list\n");
1043         MiUnlinkPageFromList(Pfn1);
1044         MiReferenceUnusedPageAndBumpLockCount(Pfn1);
1045     }
1046 
1047     /* At this point, there should no longer be any in-page errors */
1048     ASSERT(Pfn1->u4.InPageError == 0);
1049 
1050     /* Check if this was a PFN with no more share references */
1051     if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
1052 
1053     /* Bump the share count and make the page valid */
1054     Pfn1->u2.ShareCount++;
1055     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1056 
1057     /* Prototype PTEs are in paged pool, which itself might be in transition */
1058     if (FaultingAddress >= MmSystemRangeStart)
1059     {
1060         /* Check if this is a paged pool PTE in transition state */
1061         PointerToPteForProtoPage = MiAddressToPte(PointerPte);
1062         TempPte = *PointerToPteForProtoPage;
1063         if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
1064         {
1065             /* This isn't yet supported */
1066             DPRINT1("Double transition fault not yet supported\n");
1067             ASSERT(FALSE);
1068         }
1069     }
1070 
1071     /* Build the final PTE */
1072     ASSERT(PointerPte->u.Hard.Valid == 0);
1073     ASSERT(PointerPte->u.Trans.Prototype == 0);
1074     ASSERT(PointerPte->u.Trans.Transition == 1);
1075     TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
1076                      (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
1077                      MiDetermineUserGlobalPteMask(PointerPte);
1078 
1079     /* Is the PTE writeable? */
1080     if ((Pfn1->u3.e1.Modified) &&
1081         MI_IS_PAGE_WRITEABLE(&TempPte) &&
1082         !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1083     {
1084         /* Make it dirty */
1085         MI_MAKE_DIRTY_PAGE(&TempPte);
1086     }
1087     else
1088     {
1089         /* Make it clean */
1090         MI_MAKE_CLEAN_PAGE(&TempPte);
1091     }
1092 
1093     /* Write the valid PTE */
1094     MI_WRITE_VALID_PTE(PointerPte, TempPte);
1095 
1096     /* Return success */
1097     return STATUS_PAGE_FAULT_TRANSITION;
1098 }
1099 
1100 static
1101 NTSTATUS
1102 NTAPI
1103 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
1104                        IN PVOID Address,
1105                        IN PMMPTE PointerPte,
1106                        IN PMMPTE PointerProtoPte,
1107                        IN OUT PMMPFN *OutPfn,
1108                        OUT PVOID *PageFileData,
1109                        OUT PMMPTE PteValue,
1110                        IN PEPROCESS Process,
1111                        IN KIRQL OldIrql,
1112                        IN PVOID TrapInformation)
1113 {
1114     MMPTE TempPte, PteContents;
1115     PMMPFN Pfn1;
1116     PFN_NUMBER PageFrameIndex;
1117     NTSTATUS Status;
1118     PKEVENT* InPageBlock = NULL;
1119     ULONG Protection;
1120 
1121     /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1122     MI_ASSERT_PFN_LOCK_HELD();
1123     ASSERT(PointerPte->u.Hard.Valid == 0);
1124     ASSERT(PointerPte->u.Soft.Prototype == 1);
1125 
1126     /* Read the prototype PTE and check if it's valid */
1127     TempPte = *PointerProtoPte;
1128     if (TempPte.u.Hard.Valid == 1)
1129     {
1130         /* One more user of this mapped page */
1131         PageFrameIndex = PFN_FROM_PTE(&TempPte);
1132         Pfn1 = MiGetPfnEntry(PageFrameIndex);
1133         Pfn1->u2.ShareCount++;
1134 
1135         /* Call it a transition */
1136         InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
1137 
1138         /* Complete the prototype PTE fault -- this will release the PFN lock */
1139         return MiCompleteProtoPteFault(StoreInstruction,
1140                                        Address,
1141                                        PointerPte,
1142                                        PointerProtoPte,
1143                                        OldIrql,
1144                                        OutPfn);
1145     }
1146 
1147     /* Make sure there's some protection mask */
1148     if (TempPte.u.Long == 0)
1149     {
1150         /* Release the lock */
1151         DPRINT1("Access on reserved section?\n");
1152         MiReleasePfnLock(OldIrql);
1153         return STATUS_ACCESS_VIOLATION;
1154     }
1155 
1156     /* There is no such thing as a decommitted prototype PTE */
1157     ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
1158 
1159     /* Check for access rights on the PTE proper */
1160     PteContents = *PointerPte;
1161     if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
1162     {
1163         if (!PteContents.u.Proto.ReadOnly)
1164         {
1165             Protection = TempPte.u.Soft.Protection;
1166         }
1167         else
1168         {
1169             Protection = MM_READONLY;
1170         }
1171         /* Check for page acess in software */
1172         Status = MiAccessCheck(PointerProtoPte,
1173                                StoreInstruction,
1174                                KernelMode,
1175                                TempPte.u.Soft.Protection,
1176                                TrapInformation,
1177                                TRUE);
1178         ASSERT(Status == STATUS_SUCCESS);
1179     }
1180     else
1181     {
1182         Protection = PteContents.u.Soft.Protection;
1183     }
1184 
1185     /* Check for writing copy on write page */
1186     if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
1187     {
1188         PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
1189         ULONG Color;
1190 
1191         /* Resolve the proto fault as if it was a read operation */
1192         Status = MiResolveProtoPteFault(FALSE,
1193                                         Address,
1194                                         PointerPte,
1195                                         PointerProtoPte,
1196                                         OutPfn,
1197                                         PageFileData,
1198                                         PteValue,
1199                                         Process,
1200                                         OldIrql,
1201                                         TrapInformation);
1202 
1203         if (!NT_SUCCESS(Status))
1204         {
1205             return Status;
1206         }
1207 
1208         /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1209         OldIrql = MiAcquirePfnLock();
1210 
1211         /* And re-read the proto PTE */
1212         TempPte = *PointerProtoPte;
1213         ASSERT(TempPte.u.Hard.Valid == 1);
1214         ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
1215 
1216         MI_SET_USAGE(MI_USAGE_COW);
1217         MI_SET_PROCESS(Process);
1218 
1219         /* Get a new page for the private copy */
1220         if (Process > HYDRA_PROCESS)
1221             Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1222         else
1223             Color = MI_GET_NEXT_COLOR();
1224 
1225         PageFrameIndex = MiRemoveAnyPage(Color);
1226 
1227         /* Perform the copy */
1228         MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
1229 
1230         /* This will drop everything MiResolveProtoPteFault referenced */
1231         MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
1232 
1233         /* Because now we use this */
1234         Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1235         MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
1236 
1237         /* Fix the protection */
1238         Protection &= ~MM_WRITECOPY;
1239         Protection |= MM_READWRITE;
1240         if (Address < MmSystemRangeStart)
1241         {
1242             /* Build the user PTE */
1243             MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex);
1244         }
1245         else
1246         {
1247             /* Build the kernel PTE */
1248             MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex);
1249         }
1250 
1251         /* And finally, write the valid PTE */
1252         MI_WRITE_VALID_PTE(PointerPte, PteContents);
1253 
1254         /* The caller expects us to release the PFN lock */
1255         MiReleasePfnLock(OldIrql);
1256         return Status;
1257     }
1258 
1259     /* Check for clone PTEs */
1260     if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
1261 
1262     /* We don't support mapped files yet */
1263     ASSERT(TempPte.u.Soft.Prototype == 0);
1264 
1265     /* We might however have transition PTEs */
1266     if (TempPte.u.Soft.Transition == 1)
1267     {
1268         /* Resolve the transition fault */
1269         ASSERT(OldIrql != MM_NOIRQL);
1270         Status = MiResolveTransitionFault(StoreInstruction,
1271                                           Address,
1272                                           PointerProtoPte,
1273                                           Process,
1274                                           OldIrql,
1275                                           &InPageBlock);
1276         ASSERT(NT_SUCCESS(Status));
1277     }
1278     else
1279     {
1280         /* We also don't support paged out pages */
1281         ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1282 
1283         /* Resolve the demand zero fault */
1284         Status = MiResolveDemandZeroFault(Address,
1285                                           PointerProtoPte,
1286                                           (ULONG)TempPte.u.Soft.Protection,
1287                                           Process,
1288                                           OldIrql);
1289         ASSERT(NT_SUCCESS(Status));
1290     }
1291 
1292     /* Complete the prototype PTE fault -- this will release the PFN lock */
1293     ASSERT(PointerPte->u.Hard.Valid == 0);
1294     return MiCompleteProtoPteFault(StoreInstruction,
1295                                    Address,
1296                                    PointerPte,
1297                                    PointerProtoPte,
1298                                    OldIrql,
1299                                    OutPfn);
1300 }
1301 
1302 NTSTATUS
1303 NTAPI
1304 MiDispatchFault(IN ULONG FaultCode,
1305                 IN PVOID Address,
1306                 IN PMMPTE PointerPte,
1307                 IN PMMPTE PointerProtoPte,
1308                 IN BOOLEAN Recursive,
1309                 IN PEPROCESS Process,
1310                 IN PVOID TrapInformation,
1311                 IN PMMVAD Vad)
1312 {
1313     MMPTE TempPte;
1314     KIRQL OldIrql, LockIrql;
1315     NTSTATUS Status;
1316     PMMPTE SuperProtoPte;
1317     PMMPFN Pfn1, OutPfn = NULL;
1318     PFN_NUMBER PageFrameIndex;
1319     PFN_COUNT PteCount, ProcessedPtes;
1320     DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1321              Address,
1322              Process);
1323 
1324     /* Make sure the addresses are ok */
1325     ASSERT(PointerPte == MiAddressToPte(Address));
1326 
1327     //
1328     // Make sure APCs are off and we're not at dispatch
1329     //
1330     OldIrql = KeGetCurrentIrql();
1331     ASSERT(OldIrql <= APC_LEVEL);
1332     ASSERT(KeAreAllApcsDisabled() == TRUE);
1333 
1334     //
1335     // Grab a copy of the PTE
1336     //
1337     TempPte = *PointerPte;
1338 
1339     /* Do we have a prototype PTE? */
1340     if (PointerProtoPte)
1341     {
1342         /* This should never happen */
1343         ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
1344 
1345         /* Check if this is a kernel-mode address */
1346         SuperProtoPte = MiAddressToPte(PointerProtoPte);
1347         if (Address >= MmSystemRangeStart)
1348         {
1349             /* Lock the PFN database */
1350             LockIrql = MiAcquirePfnLock();
1351 
1352             /* Has the PTE been made valid yet? */
1353             if (!SuperProtoPte->u.Hard.Valid)
1354             {
1355                 ASSERT(FALSE);
1356             }
1357             else if (PointerPte->u.Hard.Valid == 1)
1358             {
1359                 ASSERT(FALSE);
1360             }
1361 
1362             /* Resolve the fault -- this will release the PFN lock */
1363             Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1364                                             Address,
1365                                             PointerPte,
1366                                             PointerProtoPte,
1367                                             &OutPfn,
1368                                             NULL,
1369                                             NULL,
1370                                             Process,
1371                                             LockIrql,
1372                                             TrapInformation);
1373             ASSERT(Status == STATUS_SUCCESS);
1374 
1375             /* Complete this as a transition fault */
1376             ASSERT(OldIrql == KeGetCurrentIrql());
1377             ASSERT(OldIrql <= APC_LEVEL);
1378             ASSERT(KeAreAllApcsDisabled() == TRUE);
1379             return Status;
1380         }
1381         else
1382         {
1383             /* We only handle the lookup path */
1384             ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
1385 
1386             /* Is there a non-image VAD? */
1387             if ((Vad) &&
1388                 (Vad->u.VadFlags.VadType != VadImageMap) &&
1389                 !(Vad->u2.VadFlags2.ExtendableFile))
1390             {
1391                 /* One day, ReactOS will cluster faults */
1392                 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
1393                 DPRINT("Should cluster fault, but won't\n");
1394             }
1395 
1396             /* Only one PTE to handle for now */
1397             PteCount = 1;
1398             ProcessedPtes = 0;
1399 
1400             /* Lock the PFN database */
1401             LockIrql = MiAcquirePfnLock();
1402 
1403             /* We only handle the valid path */
1404             ASSERT(SuperProtoPte->u.Hard.Valid == 1);
1405 
1406             /* Capture the PTE */
1407             TempPte = *PointerProtoPte;
1408 
1409             /* Loop to handle future case of clustered faults */
1410             while (TRUE)
1411             {
1412                 /* For our current usage, this should be true */
1413                 if (TempPte.u.Hard.Valid == 1)
1414                 {
1415                     /* Bump the share count on the PTE */
1416                     PageFrameIndex = PFN_FROM_PTE(&TempPte);
1417                     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1418                     Pfn1->u2.ShareCount++;
1419                 }
1420                 else if ((TempPte.u.Soft.Prototype == 0) &&
1421                          (TempPte.u.Soft.Transition == 1))
1422                 {
1423                     /* This is a standby page, bring it back from the cache */
1424                     PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1425                     DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
1426                     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1427                     ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1428 
1429                     /* Should not yet happen in ReactOS */
1430                     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
1431                     ASSERT(Pfn1->u4.InPageError == 0);
1432 
1433                     /* Get the page */
1434                     MiUnlinkPageFromList(Pfn1);
1435 
1436                     /* Bump its reference count */
1437                     ASSERT(Pfn1->u2.ShareCount == 0);
1438                     InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1439                     Pfn1->u2.ShareCount++;
1440 
1441                     /* Make it valid again */
1442                     /* This looks like another macro.... */
1443                     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1444                     ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1445                     ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1446                     ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1447                     TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1448                                      MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1449                     TempPte.u.Hard.Valid = 1;
1450                     MI_MAKE_ACCESSED_PAGE(&TempPte);
1451 
1452                     /* Is the PTE writeable? */
1453                     if ((Pfn1->u3.e1.Modified) &&
1454                         MI_IS_PAGE_WRITEABLE(&TempPte) &&
1455                         !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1456                     {
1457                         /* Make it dirty */
1458                         MI_MAKE_DIRTY_PAGE(&TempPte);
1459                     }
1460                     else
1461                     {
1462                         /* Make it clean */
1463                         MI_MAKE_CLEAN_PAGE(&TempPte);
1464                     }
1465 
1466                     /* Write the valid PTE */
1467                     MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1468                     ASSERT(PointerPte->u.Hard.Valid == 0);
1469                 }
1470                 else
1471                 {
1472                     /* Page is invalid, get out of the loop */
1473                     break;
1474                 }
1475 
1476                 /* One more done, was it the last? */
1477                 if (++ProcessedPtes == PteCount)
1478                 {
1479                     /* Complete the fault */
1480                     MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1481                                             Address,
1482                                             PointerPte,
1483                                             PointerProtoPte,
1484                                             LockIrql,
1485                                             &OutPfn);
1486 
1487                     /* THIS RELEASES THE PFN LOCK! */
1488                     break;
1489                 }
1490 
1491                 /* No clustered faults yet */
1492                 ASSERT(FALSE);
1493             }
1494 
1495             /* Did we resolve the fault? */
1496             if (ProcessedPtes)
1497             {
1498                 /* Bump the transition count */
1499                 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1500                 ProcessedPtes--;
1501 
1502                 /* Loop all the processing we did */
1503                 ASSERT(ProcessedPtes == 0);
1504 
1505                 /* Complete this as a transition fault */
1506                 ASSERT(OldIrql == KeGetCurrentIrql());
1507                 ASSERT(OldIrql <= APC_LEVEL);
1508                 ASSERT(KeAreAllApcsDisabled() == TRUE);
1509                 return STATUS_PAGE_FAULT_TRANSITION;
1510             }
1511 
1512             /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1513             OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1514             MiReferenceUsedPageAndBumpLockCount(OutPfn);
1515             ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1516             ASSERT(PointerPte->u.Hard.Valid == 0);
1517 
1518             /* Resolve the fault -- this will release the PFN lock */
1519             Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1520                                             Address,
1521                                             PointerPte,
1522                                             PointerProtoPte,
1523                                             &OutPfn,
1524                                             NULL,
1525                                             NULL,
1526                                             Process,
1527                                             LockIrql,
1528                                             TrapInformation);
1529             //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1530             //ASSERT(Status != STATUS_REFAULT);
1531             //ASSERT(Status != STATUS_PTE_CHANGED);
1532 
1533             /* Did the routine clean out the PFN or should we? */
1534             if (OutPfn)
1535             {
1536                 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1537                 ASSERT(PointerProtoPte != NULL);
1538                 OldIrql = MiAcquirePfnLock();
1539 
1540                 /* Dereference the locked PFN */
1541                 MiDereferencePfnAndDropLockCount(OutPfn);
1542                 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1543 
1544                 /* And now release the lock */
1545                 MiReleasePfnLock(OldIrql);
1546             }
1547 
1548             /* Complete this as a transition fault */
1549             ASSERT(OldIrql == KeGetCurrentIrql());
1550             ASSERT(OldIrql <= APC_LEVEL);
1551             ASSERT(KeAreAllApcsDisabled() == TRUE);
1552             return Status;
1553         }
1554     }
1555 
1556     /* Is this a transition PTE */
1557     if (TempPte.u.Soft.Transition)
1558     {
1559         PKEVENT* InPageBlock = NULL;
1560         PKEVENT PreviousPageEvent;
1561         KEVENT CurrentPageEvent;
1562 
1563         /* Lock the PFN database */
1564         LockIrql = MiAcquirePfnLock();
1565 
1566         /* Resolve */
1567         Status = MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, LockIrql, &InPageBlock);
1568 
1569         ASSERT(NT_SUCCESS(Status));
1570 
1571         if (InPageBlock != NULL)
1572         {
1573             /* Another thread is reading or writing this page. Put us into the waiting queue. */
1574             KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE);
1575             PreviousPageEvent = *InPageBlock;
1576             *InPageBlock = &CurrentPageEvent;
1577         }
1578 
1579         /* And now release the lock and leave*/
1580         MiReleasePfnLock(LockIrql);
1581 
1582         if (InPageBlock != NULL)
1583         {
1584             KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL);
1585 
1586             /* Let's the chain go on */
1587             if (PreviousPageEvent)
1588             {
1589                 KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE);
1590             }
1591         }
1592 
1593         ASSERT(OldIrql == KeGetCurrentIrql());
1594         ASSERT(OldIrql <= APC_LEVEL);
1595         ASSERT(KeAreAllApcsDisabled() == TRUE);
1596         return Status;
1597     }
1598 
1599     /* Should we page the data back in ? */
1600     if (TempPte.u.Soft.PageFileHigh != 0)
1601     {
1602         /* Lock the PFN database */
1603         LockIrql = MiAcquirePfnLock();
1604 
1605         /* Resolve */
1606         Status = MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, &LockIrql);
1607 
1608         /* And now release the lock and leave*/
1609         MiReleasePfnLock(LockIrql);
1610 
1611         ASSERT(OldIrql == KeGetCurrentIrql());
1612         ASSERT(OldIrql <= APC_LEVEL);
1613         ASSERT(KeAreAllApcsDisabled() == TRUE);
1614         return Status;
1615     }
1616 
1617     //
1618     // The PTE must be invalid but not completely empty. It must also not be a
1619     // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1620     // These are all Windows checks
1621     //
1622     ASSERT(TempPte.u.Hard.Valid == 0);
1623     ASSERT(TempPte.u.Soft.Prototype == 0);
1624     ASSERT(TempPte.u.Soft.Transition == 0);
1625     ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1626     ASSERT(TempPte.u.Long != 0);
1627 
1628     //
1629     // If we got this far, the PTE can only be a demand zero PTE, which is what
1630     // we want. Go handle it!
1631     //
1632     Status = MiResolveDemandZeroFault(Address,
1633                                       PointerPte,
1634                                       (ULONG)TempPte.u.Soft.Protection,
1635                                       Process,
1636                                       MM_NOIRQL);
1637     ASSERT(KeAreAllApcsDisabled() == TRUE);
1638     if (NT_SUCCESS(Status))
1639     {
1640         //
1641         // Make sure we're returning in a sane state and pass the status down
1642         //
1643         ASSERT(OldIrql == KeGetCurrentIrql());
1644         ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1645         return Status;
1646     }
1647 
1648     //
1649     // Generate an access fault
1650     //
1651     return STATUS_ACCESS_VIOLATION;
1652 }
1653 
1654 NTSTATUS
1655 NTAPI
1656 MmArmAccessFault(IN ULONG FaultCode,
1657                  IN PVOID Address,
1658                  IN KPROCESSOR_MODE Mode,
1659                  IN PVOID TrapInformation)
1660 {
1661     KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1662     PMMPTE ProtoPte = NULL;
1663     PMMPTE PointerPte = MiAddressToPte(Address);
1664     PMMPDE PointerPde = MiAddressToPde(Address);
1665 #if (_MI_PAGING_LEVELS >= 3)
1666     PMMPDE PointerPpe = MiAddressToPpe(Address);
1667 #if (_MI_PAGING_LEVELS == 4)
1668     PMMPDE PointerPxe = MiAddressToPxe(Address);
1669 #endif
1670 #endif
1671     MMPTE TempPte;
1672     PETHREAD CurrentThread;
1673     PEPROCESS CurrentProcess;
1674     NTSTATUS Status;
1675     PMMSUPPORT WorkingSet;
1676     ULONG ProtectionCode;
1677     PMMVAD Vad = NULL;
1678     PFN_NUMBER PageFrameIndex;
1679     ULONG Color;
1680     BOOLEAN IsSessionAddress;
1681     PMMPFN Pfn1;
1682     DPRINT("ARM3 FAULT AT: %p\n", Address);
1683 
1684     /* Check for page fault on high IRQL */
1685     if (OldIrql > APC_LEVEL)
1686     {
1687 #if (_MI_PAGING_LEVELS < 3)
1688         /* Could be a page table for paged pool, which we'll allow */
1689         if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1690         MiCheckPdeForPagedPool(Address);
1691 #endif
1692         /* Check if any of the top-level pages are invalid */
1693         if (
1694 #if (_MI_PAGING_LEVELS == 4)
1695             (PointerPxe->u.Hard.Valid == 0) ||
1696 #endif
1697 #if (_MI_PAGING_LEVELS >= 3)
1698             (PointerPpe->u.Hard.Valid == 0) ||
1699 #endif
1700             (PointerPde->u.Hard.Valid == 0) ||
1701             (PointerPte->u.Hard.Valid == 0))
1702         {
1703             /* This fault is not valid, print out some debugging help */
1704             DbgPrint("MM:***PAGE FAULT AT IRQL > 1  Va %p, IRQL %lx\n",
1705                      Address,
1706                      OldIrql);
1707             if (TrapInformation)
1708             {
1709                 PKTRAP_FRAME TrapFrame = TrapInformation;
1710 #ifdef _M_IX86
1711                 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1712                 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1713                 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1714 #elif defined(_M_AMD64)
1715                 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1716                 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1717                 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1718 #elif defined(_M_ARM)
1719                 DbgPrint("MM:***PC %p\n", TrapFrame->Pc);
1720                 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3);
1721                 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr);
1722 #endif
1723             }
1724 
1725             /* Tell the trap handler to fail */
1726             return STATUS_IN_PAGE_ERROR | 0x10000000;
1727         }
1728 
1729         /* Not yet implemented in ReactOS */
1730         ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1731         ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE);
1732 
1733         /* Check if this was a write */
1734         if (MI_IS_WRITE_ACCESS(FaultCode))
1735         {
1736             /* Was it to a read-only page? */
1737             Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1738             if (!(PointerPte->u.Long & PTE_READWRITE) &&
1739                 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1740             {
1741                 /* Crash with distinguished bugcheck code */
1742                 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1743                              (ULONG_PTR)Address,
1744                              PointerPte->u.Long,
1745                              (ULONG_PTR)TrapInformation,
1746                              10);
1747             }
1748         }
1749 
1750         /* Nothing is actually wrong */
1751         DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
1752         return STATUS_SUCCESS;
1753     }
1754 
1755     /* Check for kernel fault address */
1756     if (Address >= MmSystemRangeStart)
1757     {
1758         /* Bail out, if the fault came from user mode */
1759         if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1760 
1761 #if (_MI_PAGING_LEVELS == 2)
1762         if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1763         MiCheckPdeForPagedPool(Address);
1764 #endif
1765 
1766         /* Check if the higher page table entries are invalid */
1767         if (
1768 #if (_MI_PAGING_LEVELS == 4)
1769             /* AMD64 system, check if PXE is invalid */
1770             (PointerPxe->u.Hard.Valid == 0) ||
1771 #endif
1772 #if (_MI_PAGING_LEVELS >= 3)
1773             /* PAE/AMD64 system, check if PPE is invalid */
1774             (PointerPpe->u.Hard.Valid == 0) ||
1775 #endif
1776             /* Always check if the PDE is valid */
1777             (PointerPde->u.Hard.Valid == 0))
1778         {
1779             /* PXE/PPE/PDE (still) not valid, kill the system */
1780             KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1781                          (ULONG_PTR)Address,
1782                          FaultCode,
1783                          (ULONG_PTR)TrapInformation,
1784                          2);
1785         }
1786 
1787         /* Not handling session faults yet */
1788         IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1789 
1790         /* The PDE is valid, so read the PTE */
1791         TempPte = *PointerPte;
1792         if (TempPte.u.Hard.Valid == 1)
1793         {
1794             /* Check if this was system space or session space */
1795             if (!IsSessionAddress)
1796             {
1797                 /* Check if the PTE is still valid under PFN lock */
1798                 OldIrql = MiAcquirePfnLock();
1799                 TempPte = *PointerPte;
1800                 if (TempPte.u.Hard.Valid)
1801                 {
1802                     /* Check if this was a write */
1803                     if (MI_IS_WRITE_ACCESS(FaultCode))
1804                     {
1805                         /* Was it to a read-only page? */
1806                         Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1807                         if (!(PointerPte->u.Long & PTE_READWRITE) &&
1808                             !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1809                         {
1810                             /* Crash with distinguished bugcheck code */
1811                             KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1812                                          (ULONG_PTR)Address,
1813                                          PointerPte->u.Long,
1814                                          (ULONG_PTR)TrapInformation,
1815                                          11);
1816                         }
1817                     }
1818 
1819                     /* Check for execution of non-executable memory */
1820                     if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
1821                         !MI_IS_PAGE_EXECUTABLE(&TempPte))
1822                     {
1823                         KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
1824                                      (ULONG_PTR)Address,
1825                                      (ULONG_PTR)TempPte.u.Long,
1826                                      (ULONG_PTR)TrapInformation,
1827                                      1);
1828                     }
1829                 }
1830 
1831                 /* Release PFN lock and return all good */
1832                 MiReleasePfnLock(OldIrql);
1833                 return STATUS_SUCCESS;
1834             }
1835         }
1836 #if (_MI_PAGING_LEVELS == 2)
1837         /* Check if this was a session PTE that needs to remap the session PDE */
1838         if (MI_IS_SESSION_PTE(Address))
1839         {
1840             /* Do the remapping */
1841             Status = MiCheckPdeForSessionSpace(Address);
1842             if (!NT_SUCCESS(Status))
1843             {
1844                 /* It failed, this address is invalid */
1845                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1846                              (ULONG_PTR)Address,
1847                              FaultCode,
1848                              (ULONG_PTR)TrapInformation,
1849                              6);
1850             }
1851         }
1852 #else
1853 
1854 _WARN("Session space stuff is not implemented yet!")
1855 
1856 #endif
1857 
1858         /* Check for a fault on the page table or hyperspace */
1859         if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1860         {
1861 #if (_MI_PAGING_LEVELS < 3)
1862             /* Windows does this check but I don't understand why -- it's done above! */
1863             ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1864 #endif
1865             /* Handle this as a user mode fault */
1866             goto UserFault;
1867         }
1868 
1869         /* Get the current thread */
1870         CurrentThread = PsGetCurrentThread();
1871 
1872         /* What kind of address is this */
1873         if (!IsSessionAddress)
1874         {
1875             /* Use the system working set */
1876             WorkingSet = &MmSystemCacheWs;
1877             CurrentProcess = NULL;
1878 
1879             /* Make sure we don't have a recursive working set lock */
1880             if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1881                 (CurrentThread->OwnsProcessWorkingSetShared) ||
1882                 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1883                 (CurrentThread->OwnsSystemWorkingSetShared) ||
1884                 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1885                 (CurrentThread->OwnsSessionWorkingSetShared))
1886             {
1887                 /* Fail */
1888                 return STATUS_IN_PAGE_ERROR | 0x10000000;
1889             }
1890         }
1891         else
1892         {
1893             /* Use the session process and working set */
1894             CurrentProcess = HYDRA_PROCESS;
1895             WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1896 
1897             /* Make sure we don't have a recursive working set lock */
1898             if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1899                 (CurrentThread->OwnsSessionWorkingSetShared))
1900             {
1901                 /* Fail */
1902                 return STATUS_IN_PAGE_ERROR | 0x10000000;
1903             }
1904         }
1905 
1906         /* Acquire the working set lock */
1907         KeRaiseIrql(APC_LEVEL, &LockIrql);
1908         MiLockWorkingSet(CurrentThread, WorkingSet);
1909 
1910         /* Re-read PTE now that we own the lock */
1911         TempPte = *PointerPte;
1912         if (TempPte.u.Hard.Valid == 1)
1913         {
1914             /* Check if this was a write */
1915             if (MI_IS_WRITE_ACCESS(FaultCode))
1916             {
1917                 /* Was it to a read-only page that is not copy on write? */
1918                 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1919                 if (!(TempPte.u.Long & PTE_READWRITE) &&
1920                     !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1921                     !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1922                 {
1923                     /* Case not yet handled */
1924                     ASSERT(!IsSessionAddress);
1925 
1926                     /* Crash with distinguished bugcheck code */
1927                     KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1928                                  (ULONG_PTR)Address,
1929                                  TempPte.u.Long,
1930                                  (ULONG_PTR)TrapInformation,
1931                                  12);
1932                 }
1933             }
1934 
1935             /* Check for execution of non-executable memory */
1936             if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
1937                 !MI_IS_PAGE_EXECUTABLE(&TempPte))
1938             {
1939                 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
1940                              (ULONG_PTR)Address,
1941                              (ULONG_PTR)TempPte.u.Long,
1942                              (ULONG_PTR)TrapInformation,
1943                              2);
1944             }
1945 
1946             /* Check for read-only write in session space */
1947             if ((IsSessionAddress) &&
1948                 MI_IS_WRITE_ACCESS(FaultCode) &&
1949                 !MI_IS_PAGE_WRITEABLE(&TempPte))
1950             {
1951                 /* Sanity check */
1952                 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1953 
1954                 /* Was this COW? */
1955                 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1956                 {
1957                     /* Then this is not allowed */
1958                     KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1959                                  (ULONG_PTR)Address,
1960                                  (ULONG_PTR)TempPte.u.Long,
1961                                  (ULONG_PTR)TrapInformation,
1962                                  13);
1963                 }
1964 
1965                 /* Otherwise, handle COW */
1966                 ASSERT(FALSE);
1967             }
1968 
1969             /* Release the working set */
1970             MiUnlockWorkingSet(CurrentThread, WorkingSet);
1971             KeLowerIrql(LockIrql);
1972 
1973             /* Otherwise, the PDE was probably invalid, and all is good now */
1974             return STATUS_SUCCESS;
1975         }
1976 
1977         /* Check one kind of prototype PTE */
1978         if (TempPte.u.Soft.Prototype)
1979         {
1980             /* Make sure protected pool is on, and that this is a pool address */
1981             if ((MmProtectFreedNonPagedPool) &&
1982                 (((Address >= MmNonPagedPoolStart) &&
1983                   (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1984                                      MmSizeOfNonPagedPoolInBytes))) ||
1985                  ((Address >= MmNonPagedPoolExpansionStart) &&
1986                   (Address < MmNonPagedPoolEnd))))
1987             {
1988                 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1989                 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1990                              (ULONG_PTR)Address,
1991                              FaultCode,
1992                              Mode,
1993                              4);
1994             }
1995 
1996             /* Get the prototype PTE! */
1997             ProtoPte = MiProtoPteToPte(&TempPte);
1998 
1999             /* Do we need to locate the prototype PTE in session space? */
2000             if ((IsSessionAddress) &&
2001                 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
2002             {
2003                 /* Yep, go find it as well as the VAD for it */
2004                 ProtoPte = MiCheckVirtualAddress(Address,
2005                                                  &ProtectionCode,
2006                                                  &Vad);
2007                 ASSERT(ProtoPte != NULL);
2008             }
2009         }
2010         else
2011         {
2012             /* We don't implement transition PTEs */
2013             ASSERT(TempPte.u.Soft.Transition == 0);
2014 
2015             /* Check for no-access PTE */
2016             if (TempPte.u.Soft.Protection == MM_NOACCESS)
2017             {
2018                 /* Bugcheck the system! */
2019                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
2020                              (ULONG_PTR)Address,
2021                              FaultCode,
2022                              (ULONG_PTR)TrapInformation,
2023                              1);
2024             }
2025 
2026             /* Check for no protecton at all */
2027             if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS)
2028             {
2029                 /* Bugcheck the system! */
2030                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
2031                              (ULONG_PTR)Address,
2032                              FaultCode,
2033                              (ULONG_PTR)TrapInformation,
2034                              0);
2035             }
2036         }
2037 
2038         /* Check for demand page */
2039         if (MI_IS_WRITE_ACCESS(FaultCode) &&
2040             !(ProtoPte) &&
2041             !(IsSessionAddress) &&
2042             !(TempPte.u.Hard.Valid))
2043         {
2044             /* Get the protection code */
2045             ASSERT(TempPte.u.Soft.Transition == 0);
2046             if (!(TempPte.u.Soft.Protection & MM_READWRITE))
2047             {
2048                 /* Bugcheck the system! */
2049                 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
2050                              (ULONG_PTR)Address,
2051                              TempPte.u.Long,
2052                              (ULONG_PTR)TrapInformation,
2053                              14);
2054             }
2055         }
2056 
2057         /* Now do the real fault handling */
2058         Status = MiDispatchFault(FaultCode,
2059                                  Address,
2060                                  PointerPte,
2061                                  ProtoPte,
2062                                  FALSE,
2063                                  CurrentProcess,
2064                                  TrapInformation,
2065                                  NULL);
2066 
2067         /* Release the working set */
2068         ASSERT(KeAreAllApcsDisabled() == TRUE);
2069         MiUnlockWorkingSet(CurrentThread, WorkingSet);
2070         KeLowerIrql(LockIrql);
2071 
2072         /* We are done! */
2073         DPRINT("Fault resolved with status: %lx\n", Status);
2074         return Status;
2075     }
2076 
2077     /* This is a user fault */
2078 UserFault:
2079     CurrentThread = PsGetCurrentThread();
2080     CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
2081 
2082     /* Lock the working set */
2083     MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
2084 
2085     ProtectionCode = MM_INVALID_PROTECTION;
2086 
2087 #if (_MI_PAGING_LEVELS == 4)
2088     /* Check if the PXE is valid */
2089     if (PointerPxe->u.Hard.Valid == 0)
2090     {
2091         /* Right now, we only handle scenarios where the PXE is totally empty */
2092         ASSERT(PointerPxe->u.Long == 0);
2093 
2094         /* This is only possible for user mode addresses! */
2095         ASSERT(PointerPte <= MiHighestUserPte);
2096 
2097         /* Check if we have a VAD */
2098         MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2099         if (ProtectionCode == MM_NOACCESS)
2100         {
2101             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2102             return STATUS_ACCESS_VIOLATION;
2103         }
2104 
2105         /* Resolve a demand zero fault */
2106         MiResolveDemandZeroFault(PointerPpe,
2107                                  PointerPxe,
2108                                  MM_EXECUTE_READWRITE,
2109                                  CurrentProcess,
2110                                  MM_NOIRQL);
2111 
2112         /* We should come back with a valid PXE */
2113         ASSERT(PointerPxe->u.Hard.Valid == 1);
2114     }
2115 #endif
2116 
2117 #if (_MI_PAGING_LEVELS >= 3)
2118     /* Check if the PPE is valid */
2119     if (PointerPpe->u.Hard.Valid == 0)
2120     {
2121         /* Right now, we only handle scenarios where the PPE is totally empty */
2122         ASSERT(PointerPpe->u.Long == 0);
2123 
2124         /* This is only possible for user mode addresses! */
2125         ASSERT(PointerPte <= MiHighestUserPte);
2126 
2127         /* Check if we have a VAD, unless we did this already */
2128         if (ProtectionCode == MM_INVALID_PROTECTION)
2129         {
2130             MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2131         }
2132 
2133         if (ProtectionCode == MM_NOACCESS)
2134         {
2135             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2136             return STATUS_ACCESS_VIOLATION;
2137         }
2138 
2139         /* Resolve a demand zero fault */
2140         MiResolveDemandZeroFault(PointerPde,
2141                                  PointerPpe,
2142                                  MM_EXECUTE_READWRITE,
2143                                  CurrentProcess,
2144                                  MM_NOIRQL);
2145 
2146         /* We should come back with a valid PPE */
2147         ASSERT(PointerPpe->u.Hard.Valid == 1);
2148         MiIncrementPageTableReferences(PointerPde);
2149     }
2150 #endif
2151 
2152     /* Check if the PDE is invalid */
2153     if (PointerPde->u.Hard.Valid == 0)
2154     {
2155         /* Right now, we only handle scenarios where the PDE is totally empty */
2156         ASSERT(PointerPde->u.Long == 0);
2157 
2158         /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2159 #if MI_TRACE_PFNS
2160         UserPdeFault = TRUE;
2161 #endif
2162         /* Check if we have a VAD, unless we did this already */
2163         if (ProtectionCode == MM_INVALID_PROTECTION)
2164         {
2165             MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2166         }
2167 
2168         if (ProtectionCode == MM_NOACCESS)
2169         {
2170 #if (_MI_PAGING_LEVELS == 2)
2171             /* Could be a page table for paged pool */
2172             MiCheckPdeForPagedPool(Address);
2173 #endif
2174             /* Has the code above changed anything -- is this now a valid PTE? */
2175             Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2176 
2177             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2178             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2179             return Status;
2180         }
2181 
2182         /* Resolve a demand zero fault */
2183         MiResolveDemandZeroFault(PointerPte,
2184                                  PointerPde,
2185                                  MM_EXECUTE_READWRITE,
2186                                  CurrentProcess,
2187                                  MM_NOIRQL);
2188 #if _MI_PAGING_LEVELS >= 3
2189         MiIncrementPageTableReferences(PointerPte);
2190 #endif
2191 
2192 #if MI_TRACE_PFNS
2193         UserPdeFault = FALSE;
2194 #endif
2195         /* We should come back with APCs enabled, and with a valid PDE */
2196         ASSERT(KeAreAllApcsDisabled() == TRUE);
2197         ASSERT(PointerPde->u.Hard.Valid == 1);
2198     }
2199     else
2200     {
2201         /* Not yet implemented in ReactOS */
2202         ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
2203     }
2204 
2205     /* Now capture the PTE. */
2206     TempPte = *PointerPte;
2207 
2208     /* Check if the PTE is valid */
2209     if (TempPte.u.Hard.Valid)
2210     {
2211         /* Check if this is a write on a readonly PTE */
2212         if (MI_IS_WRITE_ACCESS(FaultCode))
2213         {
2214             /* Is this a copy on write PTE? */
2215             if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
2216             {
2217                 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
2218                 PMMPFN Pfn1;
2219 
2220                 LockIrql = MiAcquirePfnLock();
2221 
2222                 ASSERT(MmAvailablePages > 0);
2223 
2224                 MI_SET_USAGE(MI_USAGE_COW);
2225                 MI_SET_PROCESS(CurrentProcess);
2226 
2227                 /* Allocate a new page and copy it */
2228                 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
2229                 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
2230 
2231                 MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
2232 
2233                 /* Dereference whatever this PTE is referencing */
2234                 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
2235                 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
2236                 ASSERT(!MI_IS_PFN_DELETED(Pfn1));
2237                 ProtoPte = Pfn1->PteAddress;
2238                 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
2239 
2240                 /* And make a new shiny one with our page */
2241                 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
2242                 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
2243                 TempPte.u.Hard.Write = 1;
2244                 TempPte.u.Hard.CopyOnWrite = 0;
2245 
2246                 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2247 
2248                 MiReleasePfnLock(LockIrql);
2249 
2250                 /* Return the status */
2251                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2252                 return STATUS_PAGE_FAULT_COPY_ON_WRITE;
2253             }
2254 
2255             /* Is this a read-only PTE? */
2256             if (!MI_IS_PAGE_WRITEABLE(&TempPte))
2257             {
2258                 /* Return the status */
2259                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2260                 return STATUS_ACCESS_VIOLATION;
2261             }
2262         }
2263 
2264         /* Check for execution of non-executable memory */
2265         if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
2266             !MI_IS_PAGE_EXECUTABLE(&TempPte))
2267         {
2268             /* Return the status */
2269             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2270             return STATUS_ACCESS_VIOLATION;
2271         }
2272 
2273         /* The fault has already been resolved by a different thread */
2274         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2275         return STATUS_SUCCESS;
2276     }
2277 
2278     /* Quick check for demand-zero */
2279     if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) ||
2280         (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)))
2281     {
2282         /* Resolve the fault */
2283         MiResolveDemandZeroFault(Address,
2284                                  PointerPte,
2285                                  TempPte.u.Soft.Protection,
2286                                  CurrentProcess,
2287                                  MM_NOIRQL);
2288 
2289         /* Return the status */
2290         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2291         return STATUS_PAGE_FAULT_DEMAND_ZERO;
2292     }
2293 
2294     /* Check for zero PTE */
2295     if (TempPte.u.Long == 0)
2296     {
2297         /* Check if this address range belongs to a valid allocation (VAD) */
2298         ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2299         if (ProtectionCode == MM_NOACCESS)
2300         {
2301 #if (_MI_PAGING_LEVELS == 2)
2302             /* Could be a page table for paged pool */
2303             MiCheckPdeForPagedPool(Address);
2304 #endif
2305             /* Has the code above changed anything -- is this now a valid PTE? */
2306             Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2307 
2308             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2309             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2310             return Status;
2311         }
2312 
2313         /*
2314          * Check if this is a real user-mode address or actually a kernel-mode
2315          * page table for a user mode address
2316          */
2317         if (Address <= MM_HIGHEST_USER_ADDRESS
2318 #if _MI_PAGING_LEVELS >= 3
2319             || MiIsUserPte(Address)
2320 #if _MI_PAGING_LEVELS == 4
2321             || MiIsUserPde(Address)
2322 #endif
2323 #endif
2324         )
2325         {
2326             /* Add an additional page table reference */
2327             MiIncrementPageTableReferences(Address);
2328         }
2329 
2330         /* Is this a guard page? */
2331         if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
2332         {
2333             /* The VAD protection cannot be MM_DECOMMIT! */
2334             ASSERT(ProtectionCode != MM_DECOMMIT);
2335 
2336             /* Remove the bit */
2337             TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
2338             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2339 
2340             /* Not supported */
2341             ASSERT(ProtoPte == NULL);
2342             ASSERT(CurrentThread->ApcNeeded == 0);
2343 
2344             /* Drop the working set lock */
2345             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2346             ASSERT(KeGetCurrentIrql() == OldIrql);
2347 
2348             /* Handle stack expansion */
2349             return MiCheckForUserStackOverflow(Address, TrapInformation);
2350         }
2351 
2352         /* Did we get a prototype PTE back? */
2353         if (!ProtoPte)
2354         {
2355             /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2356             if (PointerPde == MiAddressToPde(PTE_BASE))
2357             {
2358                 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2359 #ifdef _M_ARM
2360                 _WARN("This is probably completely broken!");
2361                 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
2362 #else
2363                 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde);
2364 #endif
2365             }
2366             else
2367             {
2368                 /* No, create a new PTE. First, write the protection */
2369                 TempPte.u.Soft.Protection = ProtectionCode;
2370                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2371             }
2372 
2373             /* Lock the PFN database since we're going to grab a page */
2374             OldIrql = MiAcquirePfnLock();
2375 
2376             /* Make sure we have enough pages */
2377             ASSERT(MmAvailablePages >= 32);
2378 
2379             /* Try to get a zero page */
2380             MI_SET_USAGE(MI_USAGE_PEB_TEB);
2381             MI_SET_PROCESS2(CurrentProcess->ImageFileName);
2382             Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
2383             PageFrameIndex = MiRemoveZeroPageSafe(Color);
2384             if (!PageFrameIndex)
2385             {
2386                 /* Grab a page out of there. Later we should grab a colored zero page */
2387                 PageFrameIndex = MiRemoveAnyPage(Color);
2388                 ASSERT(PageFrameIndex);
2389 
2390                 /* Release the lock since we need to do some zeroing */
2391                 MiReleasePfnLock(OldIrql);
2392 
2393                 /* Zero out the page, since it's for user-mode */
2394                 MiZeroPfn(PageFrameIndex);
2395 
2396                 /* Grab the lock again so we can initialize the PFN entry */
2397                 OldIrql = MiAcquirePfnLock();
2398             }
2399 
2400             /* Initialize the PFN entry now */
2401             MiInitializePfn(PageFrameIndex, PointerPte, 1);
2402 
2403             /* Increment the count of pages in the process */
2404             CurrentProcess->NumberOfPrivatePages++;
2405 
2406             /* One more demand-zero fault */
2407             KeGetCurrentPrcb()->MmDemandZeroCount++;
2408 
2409             /* And we're done with the lock */
2410             MiReleasePfnLock(OldIrql);
2411 
2412             /* Fault on user PDE, or fault on user PTE? */
2413             if (PointerPte <= MiHighestUserPte)
2414             {
2415                 /* User fault, build a user PTE */
2416                 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2417                                           PointerPte,
2418                                           PointerPte->u.Soft.Protection,
2419                                           PageFrameIndex);
2420             }
2421             else
2422             {
2423                 /* This is a user-mode PDE, create a kernel PTE for it */
2424                 MI_MAKE_HARDWARE_PTE(&TempPte,
2425                                      PointerPte,
2426                                      PointerPte->u.Soft.Protection,
2427                                      PageFrameIndex);
2428             }
2429 
2430             /* Write the dirty bit for writeable pages */
2431             if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2432 
2433             /* And now write down the PTE, making the address valid */
2434             MI_WRITE_VALID_PTE(PointerPte, TempPte);
2435             Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2436             ASSERT(Pfn1->u1.Event == NULL);
2437 
2438             /* Demand zero */
2439             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2440             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2441             return STATUS_PAGE_FAULT_DEMAND_ZERO;
2442         }
2443 
2444         /* We should have a valid protection here */
2445         ASSERT(ProtectionCode != 0x100);
2446 
2447         /* Write the prototype PTE */
2448         TempPte = PrototypePte;
2449         TempPte.u.Soft.Protection = ProtectionCode;
2450         ASSERT(TempPte.u.Long != 0);
2451         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2452     }
2453     else
2454     {
2455         /* Get the protection code and check if this is a proto PTE */
2456         ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2457         if (TempPte.u.Soft.Prototype)
2458         {
2459             /* Do we need to go find the real PTE? */
2460             if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2461             {
2462                 /* Get the prototype pte and VAD for it */
2463                 ProtoPte = MiCheckVirtualAddress(Address,
2464                                                  &ProtectionCode,
2465                                                  &Vad);
2466                 if (!ProtoPte)
2467                 {
2468                     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2469                     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2470                     return STATUS_ACCESS_VIOLATION;
2471                 }
2472             }
2473             else
2474             {
2475                 /* Get the prototype PTE! */
2476                 ProtoPte = MiProtoPteToPte(&TempPte);
2477 
2478                 /* Is it read-only */
2479                 if (TempPte.u.Proto.ReadOnly)
2480                 {
2481                     /* Set read-only code */
2482                     ProtectionCode = MM_READONLY;
2483                 }
2484                 else
2485                 {
2486                     /* Set unknown protection */
2487                     ProtectionCode = 0x100;
2488                     ASSERT(CurrentProcess->CloneRoot != NULL);
2489                 }
2490             }
2491         }
2492     }
2493 
2494     /* Do we have a valid protection code? */
2495     if (ProtectionCode != 0x100)
2496     {
2497         /* Run a software access check first, including to detect guard pages */
2498         Status = MiAccessCheck(PointerPte,
2499                                !MI_IS_NOT_PRESENT_FAULT(FaultCode),
2500                                Mode,
2501                                ProtectionCode,
2502                                TrapInformation,
2503                                FALSE);
2504         if (Status != STATUS_SUCCESS)
2505         {
2506             /* Not supported */
2507             ASSERT(CurrentThread->ApcNeeded == 0);
2508 
2509             /* Drop the working set lock */
2510             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2511             ASSERT(KeGetCurrentIrql() == OldIrql);
2512 
2513             /* Did we hit a guard page? */
2514             if (Status == STATUS_GUARD_PAGE_VIOLATION)
2515             {
2516                 /* Handle stack expansion */
2517                 return MiCheckForUserStackOverflow(Address, TrapInformation);
2518             }
2519 
2520             /* Otherwise, fail back to the caller directly */
2521             return Status;
2522         }
2523     }
2524 
2525     /* Dispatch the fault */
2526     Status = MiDispatchFault(FaultCode,
2527                              Address,
2528                              PointerPte,
2529                              ProtoPte,
2530                              FALSE,
2531                              CurrentProcess,
2532                              TrapInformation,
2533                              Vad);
2534 
2535     /* Return the status */
2536     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2537     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2538     return Status;
2539 }
2540 
2541 NTSTATUS
2542 NTAPI
2543 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2544 {
2545     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2546     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2547 
2548     *ExecuteOptions = 0;
2549 
2550     if (CurrentProcess->Flags.ExecuteDisable)
2551     {
2552         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2553     }
2554 
2555     if (CurrentProcess->Flags.ExecuteEnable)
2556     {
2557         *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2558     }
2559 
2560     if (CurrentProcess->Flags.DisableThunkEmulation)
2561     {
2562         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2563     }
2564 
2565     if (CurrentProcess->Flags.Permanent)
2566     {
2567         *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2568     }
2569 
2570     if (CurrentProcess->Flags.ExecuteDispatchEnable)
2571     {
2572         *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2573     }
2574 
2575     if (CurrentProcess->Flags.ImageDispatchEnable)
2576     {
2577         *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2578     }
2579 
2580     return STATUS_SUCCESS;
2581 }
2582 
2583 NTSTATUS
2584 NTAPI
2585 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2586 {
2587     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2588     KLOCK_QUEUE_HANDLE ProcessLock;
2589     NTSTATUS Status = STATUS_ACCESS_DENIED;
2590     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2591 
2592     /* Only accept valid flags */
2593     if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2594     {
2595         /* Fail */
2596         DPRINT1("Invalid no-execute options\n");
2597         return STATUS_INVALID_PARAMETER;
2598     }
2599 
2600     /* Change the NX state in the process lock */
2601     KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock);
2602 
2603     /* Don't change anything if the permanent flag was set */
2604     if (!CurrentProcess->Flags.Permanent)
2605     {
2606         /* Start by assuming it's not disabled */
2607         CurrentProcess->Flags.ExecuteDisable = FALSE;
2608 
2609         /* Now process each flag and turn the equivalent bit on */
2610         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2611         {
2612             CurrentProcess->Flags.ExecuteDisable = TRUE;
2613         }
2614         if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2615         {
2616             CurrentProcess->Flags.ExecuteEnable = TRUE;
2617         }
2618         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2619         {
2620             CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2621         }
2622         if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2623         {
2624             CurrentProcess->Flags.Permanent = TRUE;
2625         }
2626         if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2627         {
2628             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2629         }
2630         if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2631         {
2632             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2633         }
2634 
2635         /* These are turned on by default if no-execution is also eanbled */
2636         if (CurrentProcess->Flags.ExecuteEnable)
2637         {
2638             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2639             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2640         }
2641 
2642         /* All good */
2643         Status = STATUS_SUCCESS;
2644     }
2645 
2646     /* Release the lock and return status */
2647     KiReleaseProcessLock(&ProcessLock);
2648     return Status;
2649 }
2650 
2651 /* EOF */
2652