xref: /reactos/ntoskrnl/mm/ARM3/pagfault.c (revision 7eff8a36)
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     }
2149 #endif
2150 
2151     /* Check if the PDE is invalid */
2152     if (PointerPde->u.Hard.Valid == 0)
2153     {
2154         /* Right now, we only handle scenarios where the PDE is totally empty */
2155         ASSERT(PointerPde->u.Long == 0);
2156 
2157         /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2158 #if MI_TRACE_PFNS
2159         UserPdeFault = TRUE;
2160 #endif
2161         /* Check if we have a VAD, unless we did this already */
2162         if (ProtectionCode == MM_INVALID_PROTECTION)
2163         {
2164             MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2165         }
2166 
2167         if (ProtectionCode == MM_NOACCESS)
2168         {
2169 #if (_MI_PAGING_LEVELS == 2)
2170             /* Could be a page table for paged pool */
2171             MiCheckPdeForPagedPool(Address);
2172 #endif
2173             /* Has the code above changed anything -- is this now a valid PTE? */
2174             Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2175 
2176             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2177             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2178             return Status;
2179         }
2180 
2181         /* Resolve a demand zero fault */
2182         MiResolveDemandZeroFault(PointerPte,
2183                                  PointerPde,
2184                                  MM_EXECUTE_READWRITE,
2185                                  CurrentProcess,
2186                                  MM_NOIRQL);
2187 #if MI_TRACE_PFNS
2188         UserPdeFault = FALSE;
2189 #endif
2190         /* We should come back with APCs enabled, and with a valid PDE */
2191         ASSERT(KeAreAllApcsDisabled() == TRUE);
2192         ASSERT(PointerPde->u.Hard.Valid == 1);
2193     }
2194     else
2195     {
2196         /* Not yet implemented in ReactOS */
2197         ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
2198     }
2199 
2200     /* Now capture the PTE. */
2201     TempPte = *PointerPte;
2202 
2203     /* Check if the PTE is valid */
2204     if (TempPte.u.Hard.Valid)
2205     {
2206         /* Check if this is a write on a readonly PTE */
2207         if (MI_IS_WRITE_ACCESS(FaultCode))
2208         {
2209             /* Is this a copy on write PTE? */
2210             if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
2211             {
2212                 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
2213                 PMMPFN Pfn1;
2214 
2215                 LockIrql = MiAcquirePfnLock();
2216 
2217                 ASSERT(MmAvailablePages > 0);
2218 
2219                 MI_SET_USAGE(MI_USAGE_COW);
2220                 MI_SET_PROCESS(CurrentProcess);
2221 
2222                 /* Allocate a new page and copy it */
2223                 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
2224                 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
2225 
2226                 MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
2227 
2228                 /* Dereference whatever this PTE is referencing */
2229                 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
2230                 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
2231                 ASSERT(!MI_IS_PFN_DELETED(Pfn1));
2232                 ProtoPte = Pfn1->PteAddress;
2233                 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
2234 
2235                 /* And make a new shiny one with our page */
2236                 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
2237                 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
2238                 TempPte.u.Hard.Write = 1;
2239                 TempPte.u.Hard.CopyOnWrite = 0;
2240 
2241                 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2242 
2243                 MiReleasePfnLock(LockIrql);
2244 
2245                 /* Return the status */
2246                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2247                 return STATUS_PAGE_FAULT_COPY_ON_WRITE;
2248             }
2249 
2250             /* Is this a read-only PTE? */
2251             if (!MI_IS_PAGE_WRITEABLE(&TempPte))
2252             {
2253                 /* Return the status */
2254                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2255                 return STATUS_ACCESS_VIOLATION;
2256             }
2257         }
2258 
2259         /* Check for execution of non-executable memory */
2260         if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
2261             !MI_IS_PAGE_EXECUTABLE(&TempPte))
2262         {
2263             /* Return the status */
2264             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2265             return STATUS_ACCESS_VIOLATION;
2266         }
2267 
2268         /* The fault has already been resolved by a different thread */
2269         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2270         return STATUS_SUCCESS;
2271     }
2272 
2273     /* Quick check for demand-zero */
2274     if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) ||
2275         (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)))
2276     {
2277         /* Resolve the fault */
2278         MiResolveDemandZeroFault(Address,
2279                                  PointerPte,
2280                                  TempPte.u.Soft.Protection,
2281                                  CurrentProcess,
2282                                  MM_NOIRQL);
2283 
2284         /* Return the status */
2285         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2286         return STATUS_PAGE_FAULT_DEMAND_ZERO;
2287     }
2288 
2289     /* Check for zero PTE */
2290     if (TempPte.u.Long == 0)
2291     {
2292         /* Check if this address range belongs to a valid allocation (VAD) */
2293         ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2294         if (ProtectionCode == MM_NOACCESS)
2295         {
2296 #if (_MI_PAGING_LEVELS == 2)
2297             /* Could be a page table for paged pool */
2298             MiCheckPdeForPagedPool(Address);
2299 #endif
2300             /* Has the code above changed anything -- is this now a valid PTE? */
2301             Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2302 
2303             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2304             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2305             return Status;
2306         }
2307 
2308         /*
2309          * Check if this is a real user-mode address or actually a kernel-mode
2310          * page table for a user mode address
2311          */
2312         if (Address <= MM_HIGHEST_USER_ADDRESS)
2313         {
2314             /* Add an additional page table reference */
2315             MiIncrementPageTableReferences(Address);
2316         }
2317 
2318         /* Is this a guard page? */
2319         if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
2320         {
2321             /* The VAD protection cannot be MM_DECOMMIT! */
2322             ASSERT(ProtectionCode != MM_DECOMMIT);
2323 
2324             /* Remove the bit */
2325             TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
2326             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2327 
2328             /* Not supported */
2329             ASSERT(ProtoPte == NULL);
2330             ASSERT(CurrentThread->ApcNeeded == 0);
2331 
2332             /* Drop the working set lock */
2333             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2334             ASSERT(KeGetCurrentIrql() == OldIrql);
2335 
2336             /* Handle stack expansion */
2337             return MiCheckForUserStackOverflow(Address, TrapInformation);
2338         }
2339 
2340         /* Did we get a prototype PTE back? */
2341         if (!ProtoPte)
2342         {
2343             /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2344             if (PointerPde == MiAddressToPde(PTE_BASE))
2345             {
2346                 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2347 #ifdef _M_ARM
2348                 _WARN("This is probably completely broken!");
2349                 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
2350 #else
2351                 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde);
2352 #endif
2353             }
2354             else
2355             {
2356                 /* No, create a new PTE. First, write the protection */
2357                 TempPte.u.Soft.Protection = ProtectionCode;
2358                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2359             }
2360 
2361             /* Lock the PFN database since we're going to grab a page */
2362             OldIrql = MiAcquirePfnLock();
2363 
2364             /* Make sure we have enough pages */
2365             ASSERT(MmAvailablePages >= 32);
2366 
2367             /* Try to get a zero page */
2368             MI_SET_USAGE(MI_USAGE_PEB_TEB);
2369             MI_SET_PROCESS2(CurrentProcess->ImageFileName);
2370             Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
2371             PageFrameIndex = MiRemoveZeroPageSafe(Color);
2372             if (!PageFrameIndex)
2373             {
2374                 /* Grab a page out of there. Later we should grab a colored zero page */
2375                 PageFrameIndex = MiRemoveAnyPage(Color);
2376                 ASSERT(PageFrameIndex);
2377 
2378                 /* Release the lock since we need to do some zeroing */
2379                 MiReleasePfnLock(OldIrql);
2380 
2381                 /* Zero out the page, since it's for user-mode */
2382                 MiZeroPfn(PageFrameIndex);
2383 
2384                 /* Grab the lock again so we can initialize the PFN entry */
2385                 OldIrql = MiAcquirePfnLock();
2386             }
2387 
2388             /* Initialize the PFN entry now */
2389             MiInitializePfn(PageFrameIndex, PointerPte, 1);
2390 
2391             /* Increment the count of pages in the process */
2392             CurrentProcess->NumberOfPrivatePages++;
2393 
2394             /* One more demand-zero fault */
2395             KeGetCurrentPrcb()->MmDemandZeroCount++;
2396 
2397             /* And we're done with the lock */
2398             MiReleasePfnLock(OldIrql);
2399 
2400             /* Fault on user PDE, or fault on user PTE? */
2401             if (PointerPte <= MiHighestUserPte)
2402             {
2403                 /* User fault, build a user PTE */
2404                 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2405                                           PointerPte,
2406                                           PointerPte->u.Soft.Protection,
2407                                           PageFrameIndex);
2408             }
2409             else
2410             {
2411                 /* This is a user-mode PDE, create a kernel PTE for it */
2412                 MI_MAKE_HARDWARE_PTE(&TempPte,
2413                                      PointerPte,
2414                                      PointerPte->u.Soft.Protection,
2415                                      PageFrameIndex);
2416             }
2417 
2418             /* Write the dirty bit for writeable pages */
2419             if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2420 
2421             /* And now write down the PTE, making the address valid */
2422             MI_WRITE_VALID_PTE(PointerPte, TempPte);
2423             Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2424             ASSERT(Pfn1->u1.Event == NULL);
2425 
2426             /* Demand zero */
2427             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2428             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2429             return STATUS_PAGE_FAULT_DEMAND_ZERO;
2430         }
2431 
2432         /* We should have a valid protection here */
2433         ASSERT(ProtectionCode != 0x100);
2434 
2435         /* Write the prototype PTE */
2436         TempPte = PrototypePte;
2437         TempPte.u.Soft.Protection = ProtectionCode;
2438         ASSERT(TempPte.u.Long != 0);
2439         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2440     }
2441     else
2442     {
2443         /* Get the protection code and check if this is a proto PTE */
2444         ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2445         if (TempPte.u.Soft.Prototype)
2446         {
2447             /* Do we need to go find the real PTE? */
2448             if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2449             {
2450                 /* Get the prototype pte and VAD for it */
2451                 ProtoPte = MiCheckVirtualAddress(Address,
2452                                                  &ProtectionCode,
2453                                                  &Vad);
2454                 if (!ProtoPte)
2455                 {
2456                     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2457                     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2458                     return STATUS_ACCESS_VIOLATION;
2459                 }
2460             }
2461             else
2462             {
2463                 /* Get the prototype PTE! */
2464                 ProtoPte = MiProtoPteToPte(&TempPte);
2465 
2466                 /* Is it read-only */
2467                 if (TempPte.u.Proto.ReadOnly)
2468                 {
2469                     /* Set read-only code */
2470                     ProtectionCode = MM_READONLY;
2471                 }
2472                 else
2473                 {
2474                     /* Set unknown protection */
2475                     ProtectionCode = 0x100;
2476                     ASSERT(CurrentProcess->CloneRoot != NULL);
2477                 }
2478             }
2479         }
2480     }
2481 
2482     /* Do we have a valid protection code? */
2483     if (ProtectionCode != 0x100)
2484     {
2485         /* Run a software access check first, including to detect guard pages */
2486         Status = MiAccessCheck(PointerPte,
2487                                !MI_IS_NOT_PRESENT_FAULT(FaultCode),
2488                                Mode,
2489                                ProtectionCode,
2490                                TrapInformation,
2491                                FALSE);
2492         if (Status != STATUS_SUCCESS)
2493         {
2494             /* Not supported */
2495             ASSERT(CurrentThread->ApcNeeded == 0);
2496 
2497             /* Drop the working set lock */
2498             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2499             ASSERT(KeGetCurrentIrql() == OldIrql);
2500 
2501             /* Did we hit a guard page? */
2502             if (Status == STATUS_GUARD_PAGE_VIOLATION)
2503             {
2504                 /* Handle stack expansion */
2505                 return MiCheckForUserStackOverflow(Address, TrapInformation);
2506             }
2507 
2508             /* Otherwise, fail back to the caller directly */
2509             return Status;
2510         }
2511     }
2512 
2513     /* Dispatch the fault */
2514     Status = MiDispatchFault(FaultCode,
2515                              Address,
2516                              PointerPte,
2517                              ProtoPte,
2518                              FALSE,
2519                              CurrentProcess,
2520                              TrapInformation,
2521                              Vad);
2522 
2523     /* Return the status */
2524     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2525     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2526     return Status;
2527 }
2528 
2529 NTSTATUS
2530 NTAPI
2531 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2532 {
2533     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2534     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2535 
2536     *ExecuteOptions = 0;
2537 
2538     if (CurrentProcess->Flags.ExecuteDisable)
2539     {
2540         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2541     }
2542 
2543     if (CurrentProcess->Flags.ExecuteEnable)
2544     {
2545         *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2546     }
2547 
2548     if (CurrentProcess->Flags.DisableThunkEmulation)
2549     {
2550         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2551     }
2552 
2553     if (CurrentProcess->Flags.Permanent)
2554     {
2555         *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2556     }
2557 
2558     if (CurrentProcess->Flags.ExecuteDispatchEnable)
2559     {
2560         *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2561     }
2562 
2563     if (CurrentProcess->Flags.ImageDispatchEnable)
2564     {
2565         *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2566     }
2567 
2568     return STATUS_SUCCESS;
2569 }
2570 
2571 NTSTATUS
2572 NTAPI
2573 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2574 {
2575     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2576     KLOCK_QUEUE_HANDLE ProcessLock;
2577     NTSTATUS Status = STATUS_ACCESS_DENIED;
2578     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2579 
2580     /* Only accept valid flags */
2581     if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2582     {
2583         /* Fail */
2584         DPRINT1("Invalid no-execute options\n");
2585         return STATUS_INVALID_PARAMETER;
2586     }
2587 
2588     /* Change the NX state in the process lock */
2589     KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock);
2590 
2591     /* Don't change anything if the permanent flag was set */
2592     if (!CurrentProcess->Flags.Permanent)
2593     {
2594         /* Start by assuming it's not disabled */
2595         CurrentProcess->Flags.ExecuteDisable = FALSE;
2596 
2597         /* Now process each flag and turn the equivalent bit on */
2598         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2599         {
2600             CurrentProcess->Flags.ExecuteDisable = TRUE;
2601         }
2602         if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2603         {
2604             CurrentProcess->Flags.ExecuteEnable = TRUE;
2605         }
2606         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2607         {
2608             CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2609         }
2610         if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2611         {
2612             CurrentProcess->Flags.Permanent = TRUE;
2613         }
2614         if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2615         {
2616             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2617         }
2618         if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2619         {
2620             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2621         }
2622 
2623         /* These are turned on by default if no-execution is also eanbled */
2624         if (CurrentProcess->Flags.ExecuteEnable)
2625         {
2626             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2627             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2628         }
2629 
2630         /* All good */
2631         Status = STATUS_SUCCESS;
2632     }
2633 
2634     /* Release the lock and return status */
2635     KiReleaseProcessLock(&ProcessLock);
2636     return Status;
2637 }
2638 
2639 /* EOF */
2640