xref: /reactos/ntoskrnl/mm/ARM3/pagfault.c (revision 374fef2d)
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     /* Zero the page if need be */
701     if (NeedZero) MiZeroPfn(PageFrameNumber);
702 
703     /* Fault on user PDE, or fault on user PTE? */
704     if (PointerPte <= MiHighestUserPte)
705     {
706         /* User fault, build a user PTE */
707         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
708                                   PointerPte,
709                                   Protection,
710                                   PageFrameNumber);
711     }
712     else
713     {
714         /* This is a user-mode PDE, create a kernel PTE for it */
715         MI_MAKE_HARDWARE_PTE(&TempPte,
716                              PointerPte,
717                              Protection,
718                              PageFrameNumber);
719     }
720 
721     /* Set it dirty if it's a writable page */
722     if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
723 
724     /* Write it */
725     MI_WRITE_VALID_PTE(PointerPte, TempPte);
726 
727     /* Did we manually acquire the lock */
728     if (HaveLock)
729     {
730         /* Get the PFN entry */
731         Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
732 
733         /* Windows does these sanity checks */
734         ASSERT(Pfn1->u1.Event == 0);
735         ASSERT(Pfn1->u3.e1.PrototypePte == 0);
736 
737         /* Release it */
738         MiReleasePfnLock(OldIrql);
739 
740         /* Update performance counters */
741         if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
742     }
743 
744     /* Add the page to our working set, if it's not a proto PTE */
745     if ((Process > HYDRA_PROCESS) && (PointerPte == MiAddressToPte(Address)))
746     {
747         /* FIXME: Also support session VM scenario */
748         MiInsertInWorkingSetList(&Process->Vm, Address, Protection);
749     }
750 
751     //
752     // It's all good now
753     //
754     DPRINT("Demand zero page has now been paged in\n");
755     return STATUS_PAGE_FAULT_DEMAND_ZERO;
756 }
757 
758 static
759 NTSTATUS
760 NTAPI
761 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
762                         IN PVOID Address,
763                         IN PMMPTE PointerPte,
764                         IN PMMPTE PointerProtoPte,
765                         IN KIRQL OldIrql,
766                         IN PMMPFN* LockedProtoPfn)
767 {
768     MMPTE TempPte;
769     PMMPTE OriginalPte, PageTablePte;
770     ULONG_PTR Protection;
771     PFN_NUMBER PageFrameIndex;
772     PMMPFN Pfn1, Pfn2;
773     BOOLEAN OriginalProtection, DirtyPage;
774 
775     /* Must be called with an valid prototype PTE, with the PFN lock held */
776     MI_ASSERT_PFN_LOCK_HELD();
777     ASSERT(PointerProtoPte->u.Hard.Valid == 1);
778 
779     /* Get the page */
780     PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
781 
782     /* Get the PFN entry and set it as a prototype PTE */
783     Pfn1 = MiGetPfnEntry(PageFrameIndex);
784     Pfn1->u3.e1.PrototypePte = 1;
785 
786     /* Increment the share count for the page table */
787     PageTablePte = MiAddressToPte(PointerPte);
788     Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
789     Pfn2->u2.ShareCount++;
790 
791     /* Check where we should be getting the protection information from */
792     if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
793     {
794         /* Get the protection from the PTE, there's no real Proto PTE data */
795         Protection = PointerPte->u.Soft.Protection;
796 
797         /* Remember that we did not use the proto protection */
798         OriginalProtection = FALSE;
799     }
800     else
801     {
802         /* Get the protection from the original PTE link */
803         OriginalPte = &Pfn1->OriginalPte;
804         Protection = OriginalPte->u.Soft.Protection;
805 
806         /* Remember that we used the original protection */
807         OriginalProtection = TRUE;
808 
809         /* Check if this was a write on a read only proto */
810         if ((StoreInstruction) && !(Protection & MM_READWRITE))
811         {
812             /* Clear the flag */
813             StoreInstruction = 0;
814         }
815     }
816 
817     /* Check if this was a write on a non-COW page */
818     DirtyPage = FALSE;
819     if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
820     {
821         /* Then the page should be marked dirty */
822         DirtyPage = TRUE;
823 
824         /* ReactOS check */
825         ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
826     }
827 
828     /* Did we get a locked incoming PFN? */
829     if (*LockedProtoPfn)
830     {
831         /* Drop a reference */
832         ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
833         MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
834         *LockedProtoPfn = NULL;
835     }
836 
837     /* Release the PFN lock */
838     MiReleasePfnLock(OldIrql);
839 
840     /* Remove special/caching bits */
841     Protection &= ~MM_PROTECT_SPECIAL;
842 
843     /* Setup caching */
844     if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
845     {
846         /* Write combining, no caching */
847         MI_PAGE_DISABLE_CACHE(&TempPte);
848         MI_PAGE_WRITE_COMBINED(&TempPte);
849     }
850     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
851     {
852         /* Write through, no caching */
853         MI_PAGE_DISABLE_CACHE(&TempPte);
854         MI_PAGE_WRITE_THROUGH(&TempPte);
855     }
856 
857     /* Check if this is a kernel or user address */
858     if (Address < MmSystemRangeStart)
859     {
860         /* Build the user PTE */
861         MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
862     }
863     else
864     {
865         /* Build the kernel PTE */
866         MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
867     }
868 
869     /* Set the dirty flag if needed */
870     if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte);
871 
872     /* Write the PTE */
873     MI_WRITE_VALID_PTE(PointerPte, TempPte);
874 
875     /* Reset the protection if needed */
876     if (OriginalProtection) Protection = MM_ZERO_ACCESS;
877 
878     /* Return success */
879     ASSERT(PointerPte == MiAddressToPte(Address));
880     return STATUS_SUCCESS;
881 }
882 
883 static
884 NTSTATUS
885 NTAPI
886 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
887                        _In_ PVOID FaultingAddress,
888                        _In_ PMMPTE PointerPte,
889                        _In_ PEPROCESS CurrentProcess,
890                        _Inout_ KIRQL *OldIrql)
891 {
892     ULONG Color;
893     PFN_NUMBER Page;
894     NTSTATUS Status;
895     MMPTE TempPte = *PointerPte;
896     PMMPFN Pfn1;
897     ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
898     ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
899     ULONG Protection = TempPte.u.Soft.Protection;
900 
901     /* Things we don't support yet */
902     ASSERT(CurrentProcess > HYDRA_PROCESS);
903     ASSERT(*OldIrql != MM_NOIRQL);
904 
905     MI_SET_USAGE(MI_USAGE_PAGE_FILE);
906     MI_SET_PROCESS(CurrentProcess);
907 
908     /* We must hold the PFN lock */
909     MI_ASSERT_PFN_LOCK_HELD();
910 
911     /* Some sanity checks */
912     ASSERT(TempPte.u.Hard.Valid == 0);
913     ASSERT(TempPte.u.Soft.PageFileHigh != 0);
914     ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
915 
916     /* Get any page, it will be overwritten */
917     Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
918     Page = MiRemoveAnyPage(Color);
919 
920     /* Initialize this PFN */
921     MiInitializePfn(Page, PointerPte, StoreInstruction);
922 
923     /* Sets the PFN as being in IO operation */
924     Pfn1 = MI_PFN_ELEMENT(Page);
925     ASSERT(Pfn1->u1.Event == NULL);
926     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
927     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
928     Pfn1->u3.e1.ReadInProgress = 1;
929 
930     /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
931     MI_MAKE_TRANSITION_PTE(&TempPte, Page, Protection);
932 
933     MI_WRITE_INVALID_PTE(PointerPte, TempPte);
934 
935     /* Release the PFN lock while we proceed */
936     MiReleasePfnLock(*OldIrql);
937 
938     /* Do the paging IO */
939     Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset);
940 
941     /* Lock the PFN database again */
942     *OldIrql = MiAcquirePfnLock();
943 
944     /* Nobody should have changed that while we were not looking */
945     ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
946     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
947 
948     if (!NT_SUCCESS(Status))
949     {
950         /* Malheur! */
951         ASSERT(FALSE);
952         Pfn1->u4.InPageError = 1;
953         Pfn1->u1.ReadStatus = Status;
954     }
955 
956     /* And the PTE can finally be valid */
957     MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, Page);
958     MI_WRITE_VALID_PTE(PointerPte, TempPte);
959 
960     Pfn1->u3.e1.ReadInProgress = 0;
961     /* Did someone start to wait on us while we proceeded ? */
962     if (Pfn1->u1.Event)
963     {
964         /* Tell them we're done */
965         KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
966     }
967 
968     /* And we can insert this into the working set */
969     MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress, Protection);
970 
971     return Status;
972 }
973 
974 static
975 NTSTATUS
976 NTAPI
977 MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
978                          IN PVOID FaultingAddress,
979                          IN PMMPTE PointerPte,
980                          IN PEPROCESS CurrentProcess,
981                          IN KIRQL OldIrql,
982                          OUT PKEVENT **InPageBlock)
983 {
984     PFN_NUMBER PageFrameIndex;
985     PMMPFN Pfn1;
986     MMPTE TempPte;
987     PMMPTE PointerToPteForProtoPage;
988     ULONG Protection;
989 
990     DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
991             FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
992 
993     /* Windowss does this check */
994     ASSERT(*InPageBlock == NULL);
995 
996     /* ARM3 doesn't support this path */
997     ASSERT(OldIrql != MM_NOIRQL);
998 
999     /* Capture the PTE and make sure it's in transition format */
1000     TempPte = *PointerPte;
1001     ASSERT((TempPte.u.Soft.Valid == 0) &&
1002            (TempPte.u.Soft.Prototype == 0) &&
1003            (TempPte.u.Soft.Transition == 1));
1004 
1005     /* Get the PFN and the PFN entry */
1006     PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1007     DPRINT("Transition PFN: %lx\n", PageFrameIndex);
1008     Pfn1 = MiGetPfnEntry(PageFrameIndex);
1009 
1010     /* One more transition fault! */
1011     InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
1012 
1013     /* This is from ARM3 -- Windows normally handles this here */
1014     ASSERT(Pfn1->u4.InPageError == 0);
1015 
1016     /* See if we should wait before terminating the fault */
1017     if ((Pfn1->u3.e1.ReadInProgress == 1)
1018             || ((Pfn1->u3.e1.WriteInProgress == 1) && StoreInstruction))
1019     {
1020         DPRINT1("The page is currently in a page transition !\n");
1021         *InPageBlock = &Pfn1->u1.Event;
1022         if (PointerPte == Pfn1->PteAddress)
1023         {
1024             DPRINT1("And this if for this particular PTE.\n");
1025             /* The PTE will be made valid by the thread serving the fault */
1026             return STATUS_SUCCESS; // FIXME: Maybe something more descriptive
1027         }
1028     }
1029 
1030     /* Windows checks there's some free pages and this isn't an in-page error */
1031     ASSERT(MmAvailablePages > 0);
1032     ASSERT(Pfn1->u4.InPageError == 0);
1033 
1034     /* ReactOS checks for this */
1035     ASSERT(MmAvailablePages > 32);
1036 
1037     /* Was this a transition page in the valid list, or free/zero list? */
1038     if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
1039     {
1040         /* All Windows does here is a bunch of sanity checks */
1041         DPRINT("Transition in active list\n");
1042         ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
1043                (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
1044         ASSERT(Pfn1->u2.ShareCount != 0);
1045         ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
1046     }
1047     else
1048     {
1049         /* Otherwise, the page is removed from its list */
1050         DPRINT("Transition page in free/zero list\n");
1051         MiUnlinkPageFromList(Pfn1);
1052         MiReferenceUnusedPageAndBumpLockCount(Pfn1);
1053     }
1054 
1055     /* At this point, there should no longer be any in-page errors */
1056     ASSERT(Pfn1->u4.InPageError == 0);
1057 
1058     /* Check if this was a PFN with no more share references */
1059     if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
1060 
1061     /* Bump the share count and make the page valid */
1062     Pfn1->u2.ShareCount++;
1063     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1064 
1065     /* Prototype PTEs are in paged pool, which itself might be in transition */
1066     if (FaultingAddress >= MmSystemRangeStart)
1067     {
1068         /* Check if this is a paged pool PTE in transition state */
1069         PointerToPteForProtoPage = MiAddressToPte(PointerPte);
1070         TempPte = *PointerToPteForProtoPage;
1071         if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
1072         {
1073             /* This isn't yet supported */
1074             DPRINT1("Double transition fault not yet supported\n");
1075             ASSERT(FALSE);
1076         }
1077     }
1078 
1079     /* Build the final PTE */
1080     ASSERT(PointerPte->u.Hard.Valid == 0);
1081     ASSERT(PointerPte->u.Trans.Prototype == 0);
1082     ASSERT(PointerPte->u.Trans.Transition == 1);
1083     Protection = TempPte.u.Trans.Protection;
1084     TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
1085                      (MmProtectToPteMask[Protection]) |
1086                      MiDetermineUserGlobalPteMask(PointerPte);
1087 
1088     /* Is the PTE writeable? */
1089     if ((Pfn1->u3.e1.Modified) &&
1090         MI_IS_PAGE_WRITEABLE(&TempPte) &&
1091         !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1092     {
1093         /* Make it dirty */
1094         MI_MAKE_DIRTY_PAGE(&TempPte);
1095     }
1096     else
1097     {
1098         /* Make it clean */
1099         MI_MAKE_CLEAN_PAGE(&TempPte);
1100     }
1101 
1102     /* Write the valid PTE */
1103     MI_WRITE_VALID_PTE(PointerPte, TempPte);
1104 
1105     /* If this was a user fault, add it to the working set */
1106     if (CurrentProcess > HYDRA_PROCESS)
1107         MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress, Protection);
1108 
1109     /* Return success */
1110     return STATUS_PAGE_FAULT_TRANSITION;
1111 }
1112 
1113 static
1114 NTSTATUS
1115 NTAPI
1116 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
1117                        IN PVOID Address,
1118                        IN PMMPTE PointerPte,
1119                        IN PMMPTE PointerProtoPte,
1120                        IN OUT PMMPFN *OutPfn,
1121                        OUT PVOID *PageFileData,
1122                        OUT PMMPTE PteValue,
1123                        IN PEPROCESS Process,
1124                        IN KIRQL OldIrql,
1125                        IN PVOID TrapInformation)
1126 {
1127     MMPTE TempPte, PteContents;
1128     PMMPFN Pfn1;
1129     PFN_NUMBER PageFrameIndex;
1130     NTSTATUS Status;
1131     PKEVENT* InPageBlock = NULL;
1132     ULONG Protection;
1133 
1134     /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1135     MI_ASSERT_PFN_LOCK_HELD();
1136     ASSERT(PointerPte->u.Hard.Valid == 0);
1137     ASSERT(PointerPte->u.Soft.Prototype == 1);
1138 
1139     /* Read the prototype PTE and check if it's valid */
1140     TempPte = *PointerProtoPte;
1141     if (TempPte.u.Hard.Valid == 1)
1142     {
1143         /* One more user of this mapped page */
1144         PageFrameIndex = PFN_FROM_PTE(&TempPte);
1145         Pfn1 = MiGetPfnEntry(PageFrameIndex);
1146         Pfn1->u2.ShareCount++;
1147 
1148         /* Call it a transition */
1149         InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
1150 
1151         /* Complete the prototype PTE fault -- this will release the PFN lock */
1152         return MiCompleteProtoPteFault(StoreInstruction,
1153                                        Address,
1154                                        PointerPte,
1155                                        PointerProtoPte,
1156                                        OldIrql,
1157                                        OutPfn);
1158     }
1159 
1160     /* Make sure there's some protection mask */
1161     if (TempPte.u.Long == 0)
1162     {
1163         /* Release the lock */
1164         DPRINT1("Access on reserved section?\n");
1165         MiReleasePfnLock(OldIrql);
1166         return STATUS_ACCESS_VIOLATION;
1167     }
1168 
1169     /* There is no such thing as a decommitted prototype PTE */
1170     ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
1171 
1172     /* Check for access rights on the PTE proper */
1173     PteContents = *PointerPte;
1174     if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
1175     {
1176         if (!PteContents.u.Proto.ReadOnly)
1177         {
1178             Protection = TempPte.u.Soft.Protection;
1179         }
1180         else
1181         {
1182             Protection = MM_READONLY;
1183         }
1184         /* Check for page acess in software */
1185         Status = MiAccessCheck(PointerProtoPte,
1186                                StoreInstruction,
1187                                KernelMode,
1188                                TempPte.u.Soft.Protection,
1189                                TrapInformation,
1190                                TRUE);
1191         ASSERT(Status == STATUS_SUCCESS);
1192     }
1193     else
1194     {
1195         Protection = PteContents.u.Soft.Protection;
1196     }
1197 
1198     /* Check for writing copy on write page */
1199     if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
1200     {
1201         PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
1202         ULONG Color;
1203 
1204         /* Resolve the proto fault as if it was a read operation */
1205         Status = MiResolveProtoPteFault(FALSE,
1206                                         Address,
1207                                         PointerPte,
1208                                         PointerProtoPte,
1209                                         OutPfn,
1210                                         PageFileData,
1211                                         PteValue,
1212                                         Process,
1213                                         OldIrql,
1214                                         TrapInformation);
1215 
1216         if (!NT_SUCCESS(Status))
1217         {
1218             return Status;
1219         }
1220 
1221         /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1222         OldIrql = MiAcquirePfnLock();
1223 
1224         /* And re-read the proto PTE */
1225         TempPte = *PointerProtoPte;
1226         ASSERT(TempPte.u.Hard.Valid == 1);
1227         ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
1228 
1229         MI_SET_USAGE(MI_USAGE_COW);
1230         MI_SET_PROCESS(Process);
1231 
1232         /* Get a new page for the private copy */
1233         if (Process > HYDRA_PROCESS)
1234             Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1235         else
1236             Color = MI_GET_NEXT_COLOR();
1237 
1238         PageFrameIndex = MiRemoveAnyPage(Color);
1239 
1240         /* Perform the copy */
1241         MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
1242 
1243         /* This will drop everything MiResolveProtoPteFault referenced */
1244         MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
1245 
1246         /* Because now we use this */
1247         Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1248         MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
1249 
1250         /* The caller expects us to release the PFN lock */
1251         MiReleasePfnLock(OldIrql);
1252 
1253         /* Fix the protection */
1254         Protection &= ~MM_WRITECOPY;
1255         Protection |= MM_READWRITE;
1256         if (Address < MmSystemRangeStart)
1257         {
1258             /* Build the user PTE */
1259             MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex);
1260         }
1261         else
1262         {
1263             /* Build the kernel PTE */
1264             MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex);
1265         }
1266 
1267         /* And finally, write the valid PTE */
1268         MI_WRITE_VALID_PTE(PointerPte, PteContents);
1269 
1270         /* Add the page to our working set */
1271         if (Process > HYDRA_PROCESS)
1272         {
1273             /* FIXME: Also support session VM scenario */
1274             MiInsertInWorkingSetList(&Process->Vm, Address, Protection);
1275         }
1276         return Status;
1277     }
1278 
1279     /* Check for clone PTEs */
1280     if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
1281 
1282     /* We don't support mapped files yet */
1283     ASSERT(TempPte.u.Soft.Prototype == 0);
1284 
1285     /* We might however have transition PTEs */
1286     if (TempPte.u.Soft.Transition == 1)
1287     {
1288         /* Resolve the transition fault */
1289         ASSERT(OldIrql != MM_NOIRQL);
1290         Status = MiResolveTransitionFault(StoreInstruction,
1291                                           Address,
1292                                           PointerProtoPte,
1293                                           Process,
1294                                           OldIrql,
1295                                           &InPageBlock);
1296         ASSERT(NT_SUCCESS(Status));
1297     }
1298     else
1299     {
1300         /* We also don't support paged out pages */
1301         ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1302 
1303         /* Resolve the demand zero fault */
1304         Status = MiResolveDemandZeroFault(Address,
1305                                           PointerProtoPte,
1306                                           (ULONG)TempPte.u.Soft.Protection,
1307                                           Process,
1308                                           OldIrql);
1309         ASSERT(NT_SUCCESS(Status));
1310     }
1311 
1312     /* Complete the prototype PTE fault -- this will release the PFN lock */
1313     ASSERT(PointerPte->u.Hard.Valid == 0);
1314     return MiCompleteProtoPteFault(StoreInstruction,
1315                                    Address,
1316                                    PointerPte,
1317                                    PointerProtoPte,
1318                                    OldIrql,
1319                                    OutPfn);
1320 }
1321 
1322 NTSTATUS
1323 NTAPI
1324 MiDispatchFault(IN ULONG FaultCode,
1325                 IN PVOID Address,
1326                 IN PMMPTE PointerPte,
1327                 IN PMMPTE PointerProtoPte,
1328                 IN BOOLEAN Recursive,
1329                 IN PEPROCESS Process,
1330                 IN PVOID TrapInformation,
1331                 IN PMMVAD Vad)
1332 {
1333     MMPTE TempPte;
1334     KIRQL OldIrql, LockIrql;
1335     NTSTATUS Status;
1336     PMMPTE SuperProtoPte;
1337     PMMPFN Pfn1, OutPfn = NULL;
1338     PFN_NUMBER PageFrameIndex;
1339     PFN_COUNT PteCount, ProcessedPtes;
1340     DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1341              Address,
1342              Process);
1343 
1344     /* Make sure the addresses are ok */
1345     ASSERT(PointerPte == MiAddressToPte(Address));
1346 
1347     //
1348     // Make sure APCs are off and we're not at dispatch
1349     //
1350     OldIrql = KeGetCurrentIrql();
1351     ASSERT(OldIrql <= APC_LEVEL);
1352     ASSERT(KeAreAllApcsDisabled() == TRUE);
1353 
1354     //
1355     // Grab a copy of the PTE
1356     //
1357     TempPte = *PointerPte;
1358 
1359     /* Do we have a prototype PTE? */
1360     if (PointerProtoPte)
1361     {
1362         /* This should never happen */
1363         ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
1364 
1365         /* Check if this is a kernel-mode address */
1366         SuperProtoPte = MiAddressToPte(PointerProtoPte);
1367         if (Address >= MmSystemRangeStart)
1368         {
1369             /* Lock the PFN database */
1370             LockIrql = MiAcquirePfnLock();
1371 
1372             /* Has the PTE been made valid yet? */
1373             if (!SuperProtoPte->u.Hard.Valid)
1374             {
1375                 ASSERT(FALSE);
1376             }
1377             else if (PointerPte->u.Hard.Valid == 1)
1378             {
1379                 ASSERT(FALSE);
1380             }
1381 
1382             /* Resolve the fault -- this will release the PFN lock */
1383             Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1384                                             Address,
1385                                             PointerPte,
1386                                             PointerProtoPte,
1387                                             &OutPfn,
1388                                             NULL,
1389                                             NULL,
1390                                             Process,
1391                                             LockIrql,
1392                                             TrapInformation);
1393             ASSERT(Status == STATUS_SUCCESS);
1394 
1395             /* Complete this as a transition fault */
1396             ASSERT(OldIrql == KeGetCurrentIrql());
1397             ASSERT(OldIrql <= APC_LEVEL);
1398             ASSERT(KeAreAllApcsDisabled() == TRUE);
1399             return Status;
1400         }
1401         else
1402         {
1403             /* We only handle the lookup path */
1404             ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
1405 
1406             /* Is there a non-image VAD? */
1407             if ((Vad) &&
1408                 (Vad->u.VadFlags.VadType != VadImageMap) &&
1409                 !(Vad->u2.VadFlags2.ExtendableFile))
1410             {
1411                 /* One day, ReactOS will cluster faults */
1412                 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
1413                 DPRINT("Should cluster fault, but won't\n");
1414             }
1415 
1416             /* Only one PTE to handle for now */
1417             PteCount = 1;
1418             ProcessedPtes = 0;
1419 
1420             /* Lock the PFN database */
1421             LockIrql = MiAcquirePfnLock();
1422 
1423             /* We only handle the valid path */
1424             ASSERT(SuperProtoPte->u.Hard.Valid == 1);
1425 
1426             /* Capture the PTE */
1427             TempPte = *PointerProtoPte;
1428 
1429             /* Loop to handle future case of clustered faults */
1430             while (TRUE)
1431             {
1432                 /* For our current usage, this should be true */
1433                 if (TempPte.u.Hard.Valid == 1)
1434                 {
1435                     /* Bump the share count on the PTE */
1436                     PageFrameIndex = PFN_FROM_PTE(&TempPte);
1437                     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1438                     Pfn1->u2.ShareCount++;
1439                 }
1440                 else if ((TempPte.u.Soft.Prototype == 0) &&
1441                          (TempPte.u.Soft.Transition == 1))
1442                 {
1443                     /* This is a standby page, bring it back from the cache */
1444                     PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1445                     DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
1446                     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1447                     ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1448 
1449                     /* Should not yet happen in ReactOS */
1450                     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
1451                     ASSERT(Pfn1->u4.InPageError == 0);
1452 
1453                     /* Get the page */
1454                     MiUnlinkPageFromList(Pfn1);
1455 
1456                     /* Bump its reference count */
1457                     ASSERT(Pfn1->u2.ShareCount == 0);
1458                     InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1459                     Pfn1->u2.ShareCount++;
1460 
1461                     /* Make it valid again */
1462                     /* This looks like another macro.... */
1463                     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1464                     ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1465                     ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1466                     ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1467                     TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1468                                      MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1469                     TempPte.u.Hard.Valid = 1;
1470                     MI_MAKE_ACCESSED_PAGE(&TempPte);
1471 
1472                     /* Is the PTE writeable? */
1473                     if ((Pfn1->u3.e1.Modified) &&
1474                         MI_IS_PAGE_WRITEABLE(&TempPte) &&
1475                         !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1476                     {
1477                         /* Make it dirty */
1478                         MI_MAKE_DIRTY_PAGE(&TempPte);
1479                     }
1480                     else
1481                     {
1482                         /* Make it clean */
1483                         MI_MAKE_CLEAN_PAGE(&TempPte);
1484                     }
1485 
1486                     /* Write the valid PTE */
1487                     MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1488                     ASSERT(PointerPte->u.Hard.Valid == 0);
1489                 }
1490                 else
1491                 {
1492                     /* Page is invalid, get out of the loop */
1493                     break;
1494                 }
1495 
1496                 /* One more done, was it the last? */
1497                 if (++ProcessedPtes == PteCount)
1498                 {
1499                     /* Complete the fault */
1500                     MiCompleteProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1501                                             Address,
1502                                             PointerPte,
1503                                             PointerProtoPte,
1504                                             LockIrql,
1505                                             &OutPfn);
1506 
1507                     /* THIS RELEASES THE PFN LOCK! */
1508                     break;
1509                 }
1510 
1511                 /* No clustered faults yet */
1512                 ASSERT(FALSE);
1513             }
1514 
1515             /* Did we resolve the fault? */
1516             if (ProcessedPtes)
1517             {
1518                 /* Bump the transition count */
1519                 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1520                 ProcessedPtes--;
1521 
1522                 /* Loop all the processing we did */
1523                 ASSERT(ProcessedPtes == 0);
1524 
1525                 /* Complete this as a transition fault */
1526                 ASSERT(OldIrql == KeGetCurrentIrql());
1527                 ASSERT(OldIrql <= APC_LEVEL);
1528                 ASSERT(KeAreAllApcsDisabled() == TRUE);
1529                 return STATUS_PAGE_FAULT_TRANSITION;
1530             }
1531 
1532             /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1533             OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1534             MiReferenceUsedPageAndBumpLockCount(OutPfn);
1535             ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1536             ASSERT(PointerPte->u.Hard.Valid == 0);
1537 
1538             /* Resolve the fault -- this will release the PFN lock */
1539             Status = MiResolveProtoPteFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode),
1540                                             Address,
1541                                             PointerPte,
1542                                             PointerProtoPte,
1543                                             &OutPfn,
1544                                             NULL,
1545                                             NULL,
1546                                             Process,
1547                                             LockIrql,
1548                                             TrapInformation);
1549             //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1550             //ASSERT(Status != STATUS_REFAULT);
1551             //ASSERT(Status != STATUS_PTE_CHANGED);
1552 
1553             /* Did the routine clean out the PFN or should we? */
1554             if (OutPfn)
1555             {
1556                 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1557                 ASSERT(PointerProtoPte != NULL);
1558                 OldIrql = MiAcquirePfnLock();
1559 
1560                 /* Dereference the locked PFN */
1561                 MiDereferencePfnAndDropLockCount(OutPfn);
1562                 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1563 
1564                 /* And now release the lock */
1565                 MiReleasePfnLock(OldIrql);
1566             }
1567 
1568             /* Complete this as a transition fault */
1569             ASSERT(OldIrql == KeGetCurrentIrql());
1570             ASSERT(OldIrql <= APC_LEVEL);
1571             ASSERT(KeAreAllApcsDisabled() == TRUE);
1572             return Status;
1573         }
1574     }
1575 
1576     /* Is this a transition PTE */
1577     if (TempPte.u.Soft.Transition)
1578     {
1579         PKEVENT* InPageBlock = NULL;
1580         PKEVENT PreviousPageEvent;
1581         KEVENT CurrentPageEvent;
1582 
1583         /* Lock the PFN database */
1584         LockIrql = MiAcquirePfnLock();
1585 
1586         /* Resolve */
1587         Status = MiResolveTransitionFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, LockIrql, &InPageBlock);
1588 
1589         ASSERT(NT_SUCCESS(Status));
1590 
1591         if (InPageBlock != NULL)
1592         {
1593             /* Another thread is reading or writing this page. Put us into the waiting queue. */
1594             KeInitializeEvent(&CurrentPageEvent, NotificationEvent, FALSE);
1595             PreviousPageEvent = *InPageBlock;
1596             *InPageBlock = &CurrentPageEvent;
1597         }
1598 
1599         /* And now release the lock and leave*/
1600         MiReleasePfnLock(LockIrql);
1601 
1602         if (InPageBlock != NULL)
1603         {
1604             KeWaitForSingleObject(&CurrentPageEvent, WrPageIn, KernelMode, FALSE, NULL);
1605 
1606             /* Let's the chain go on */
1607             if (PreviousPageEvent)
1608             {
1609                 KeSetEvent(PreviousPageEvent, IO_NO_INCREMENT, FALSE);
1610             }
1611         }
1612 
1613         ASSERT(OldIrql == KeGetCurrentIrql());
1614         ASSERT(OldIrql <= APC_LEVEL);
1615         ASSERT(KeAreAllApcsDisabled() == TRUE);
1616         return Status;
1617     }
1618 
1619     /* Should we page the data back in ? */
1620     if (TempPte.u.Soft.PageFileHigh != 0)
1621     {
1622         /* Lock the PFN database */
1623         LockIrql = MiAcquirePfnLock();
1624 
1625         /* Resolve */
1626         Status = MiResolvePageFileFault(!MI_IS_NOT_PRESENT_FAULT(FaultCode), Address, PointerPte, Process, &LockIrql);
1627 
1628         /* And now release the lock and leave*/
1629         MiReleasePfnLock(LockIrql);
1630 
1631         ASSERT(OldIrql == KeGetCurrentIrql());
1632         ASSERT(OldIrql <= APC_LEVEL);
1633         ASSERT(KeAreAllApcsDisabled() == TRUE);
1634         return Status;
1635     }
1636 
1637     //
1638     // The PTE must be invalid but not completely empty. It must also not be a
1639     // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1640     // These are all Windows checks
1641     //
1642     ASSERT(TempPte.u.Hard.Valid == 0);
1643     ASSERT(TempPte.u.Soft.Prototype == 0);
1644     ASSERT(TempPte.u.Soft.Transition == 0);
1645     ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1646     ASSERT(TempPte.u.Long != 0);
1647 
1648     //
1649     // If we got this far, the PTE can only be a demand zero PTE, which is what
1650     // we want. Go handle it!
1651     //
1652     Status = MiResolveDemandZeroFault(Address,
1653                                       PointerPte,
1654                                       (ULONG)TempPte.u.Soft.Protection,
1655                                       Process,
1656                                       MM_NOIRQL);
1657     ASSERT(KeAreAllApcsDisabled() == TRUE);
1658     if (NT_SUCCESS(Status))
1659     {
1660         //
1661         // Make sure we're returning in a sane state and pass the status down
1662         //
1663         ASSERT(OldIrql == KeGetCurrentIrql());
1664         ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1665         return Status;
1666     }
1667 
1668     //
1669     // Generate an access fault
1670     //
1671     return STATUS_ACCESS_VIOLATION;
1672 }
1673 
1674 NTSTATUS
1675 NTAPI
1676 MmArmAccessFault(IN ULONG FaultCode,
1677                  IN PVOID Address,
1678                  IN KPROCESSOR_MODE Mode,
1679                  IN PVOID TrapInformation)
1680 {
1681     KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1682     PMMPTE ProtoPte = NULL;
1683     PMMPTE PointerPte = MiAddressToPte(Address);
1684     PMMPDE PointerPde = MiAddressToPde(Address);
1685 #if (_MI_PAGING_LEVELS >= 3)
1686     PMMPDE PointerPpe = MiAddressToPpe(Address);
1687 #if (_MI_PAGING_LEVELS == 4)
1688     PMMPDE PointerPxe = MiAddressToPxe(Address);
1689 #endif
1690 #endif
1691     MMPTE TempPte;
1692     PETHREAD CurrentThread;
1693     PEPROCESS CurrentProcess;
1694     NTSTATUS Status;
1695     PMMSUPPORT WorkingSet;
1696     ULONG ProtectionCode;
1697     PMMVAD Vad = NULL;
1698     PFN_NUMBER PageFrameIndex;
1699     ULONG Color;
1700     BOOLEAN IsSessionAddress;
1701     PMMPFN Pfn1;
1702     DPRINT("ARM3 FAULT AT: %p\n", Address);
1703 
1704     /* Check for page fault on high IRQL */
1705     if (OldIrql > APC_LEVEL)
1706     {
1707 #if (_MI_PAGING_LEVELS < 3)
1708         /* Could be a page table for paged pool, which we'll allow */
1709         if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1710         MiCheckPdeForPagedPool(Address);
1711 #endif
1712         /* Check if any of the top-level pages are invalid */
1713         if (
1714 #if (_MI_PAGING_LEVELS == 4)
1715             (PointerPxe->u.Hard.Valid == 0) ||
1716 #endif
1717 #if (_MI_PAGING_LEVELS >= 3)
1718             (PointerPpe->u.Hard.Valid == 0) ||
1719 #endif
1720             (PointerPde->u.Hard.Valid == 0) ||
1721             (PointerPte->u.Hard.Valid == 0))
1722         {
1723             /* This fault is not valid, print out some debugging help */
1724             DbgPrint("MM:***PAGE FAULT AT IRQL > 1  Va %p, IRQL %lx\n",
1725                      Address,
1726                      OldIrql);
1727             if (TrapInformation)
1728             {
1729                 PKTRAP_FRAME TrapFrame = TrapInformation;
1730 #ifdef _M_IX86
1731                 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1732                 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1733                 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1734 #elif defined(_M_AMD64)
1735                 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1736                 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1737                 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1738 #elif defined(_M_ARM)
1739                 DbgPrint("MM:***PC %p\n", TrapFrame->Pc);
1740                 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3);
1741                 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr);
1742 #endif
1743             }
1744 
1745             /* Tell the trap handler to fail */
1746             return STATUS_IN_PAGE_ERROR | 0x10000000;
1747         }
1748 
1749         /* Not yet implemented in ReactOS */
1750         ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1751         ASSERT((!MI_IS_NOT_PRESENT_FAULT(FaultCode) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE);
1752 
1753         /* Check if this was a write */
1754         if (MI_IS_WRITE_ACCESS(FaultCode))
1755         {
1756             /* Was it to a read-only page? */
1757             Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1758             if (!(PointerPte->u.Long & PTE_READWRITE) &&
1759                 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1760             {
1761                 /* Crash with distinguished bugcheck code */
1762                 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1763                              (ULONG_PTR)Address,
1764                              PointerPte->u.Long,
1765                              (ULONG_PTR)TrapInformation,
1766                              10);
1767             }
1768         }
1769 
1770         /* Nothing is actually wrong */
1771         DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
1772         return STATUS_SUCCESS;
1773     }
1774 
1775     /* Check for kernel fault address */
1776     if (Address >= MmSystemRangeStart)
1777     {
1778         /* Bail out, if the fault came from user mode */
1779         if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1780 
1781 #if (_MI_PAGING_LEVELS == 2)
1782         if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1783         MiCheckPdeForPagedPool(Address);
1784 #endif
1785 
1786         /* Check if the higher page table entries are invalid */
1787         if (
1788 #if (_MI_PAGING_LEVELS == 4)
1789             /* AMD64 system, check if PXE is invalid */
1790             (PointerPxe->u.Hard.Valid == 0) ||
1791 #endif
1792 #if (_MI_PAGING_LEVELS >= 3)
1793             /* PAE/AMD64 system, check if PPE is invalid */
1794             (PointerPpe->u.Hard.Valid == 0) ||
1795 #endif
1796             /* Always check if the PDE is valid */
1797             (PointerPde->u.Hard.Valid == 0))
1798         {
1799             /* PXE/PPE/PDE (still) not valid, kill the system */
1800             KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1801                          (ULONG_PTR)Address,
1802                          FaultCode,
1803                          (ULONG_PTR)TrapInformation,
1804                          2);
1805         }
1806 
1807         /* Not handling session faults yet */
1808         IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1809 
1810         /* The PDE is valid, so read the PTE */
1811         TempPte = *PointerPte;
1812         if (TempPte.u.Hard.Valid == 1)
1813         {
1814             /* Check if this was system space or session space */
1815             if (!IsSessionAddress)
1816             {
1817                 /* Check if the PTE is still valid under PFN lock */
1818                 OldIrql = MiAcquirePfnLock();
1819                 TempPte = *PointerPte;
1820                 if (TempPte.u.Hard.Valid)
1821                 {
1822                     /* Check if this was a write */
1823                     if (MI_IS_WRITE_ACCESS(FaultCode))
1824                     {
1825                         /* Was it to a read-only page? */
1826                         Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1827                         if (!(PointerPte->u.Long & PTE_READWRITE) &&
1828                             !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1829                         {
1830                             /* Crash with distinguished bugcheck code */
1831                             KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1832                                          (ULONG_PTR)Address,
1833                                          PointerPte->u.Long,
1834                                          (ULONG_PTR)TrapInformation,
1835                                          11);
1836                         }
1837                     }
1838 
1839                     /* Check for execution of non-executable memory */
1840                     if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
1841                         !MI_IS_PAGE_EXECUTABLE(&TempPte))
1842                     {
1843                         KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
1844                                      (ULONG_PTR)Address,
1845                                      (ULONG_PTR)TempPte.u.Long,
1846                                      (ULONG_PTR)TrapInformation,
1847                                      1);
1848                     }
1849                 }
1850 
1851                 /* Release PFN lock and return all good */
1852                 MiReleasePfnLock(OldIrql);
1853                 return STATUS_SUCCESS;
1854             }
1855         }
1856 #if (_MI_PAGING_LEVELS == 2)
1857         /* Check if this was a session PTE that needs to remap the session PDE */
1858         if (MI_IS_SESSION_PTE(Address))
1859         {
1860             /* Do the remapping */
1861             Status = MiCheckPdeForSessionSpace(Address);
1862             if (!NT_SUCCESS(Status))
1863             {
1864                 /* It failed, this address is invalid */
1865                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1866                              (ULONG_PTR)Address,
1867                              FaultCode,
1868                              (ULONG_PTR)TrapInformation,
1869                              6);
1870             }
1871         }
1872 #else
1873 
1874 _WARN("Session space stuff is not implemented yet!")
1875 
1876 #endif
1877 
1878         /* Check for a fault on the page table or hyperspace */
1879         if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1880         {
1881 #if (_MI_PAGING_LEVELS < 3)
1882             /* Windows does this check but I don't understand why -- it's done above! */
1883             ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1884 #endif
1885             /* Handle this as a user mode fault */
1886             goto UserFault;
1887         }
1888 
1889         /* Get the current thread */
1890         CurrentThread = PsGetCurrentThread();
1891 
1892         /* What kind of address is this */
1893         if (!IsSessionAddress)
1894         {
1895             /* Use the system working set */
1896             WorkingSet = &MmSystemCacheWs;
1897             CurrentProcess = NULL;
1898 
1899             /* Make sure we don't have a recursive working set lock */
1900             if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1901                 (CurrentThread->OwnsProcessWorkingSetShared) ||
1902                 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1903                 (CurrentThread->OwnsSystemWorkingSetShared) ||
1904                 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1905                 (CurrentThread->OwnsSessionWorkingSetShared))
1906             {
1907                 /* Fail */
1908                 return STATUS_IN_PAGE_ERROR | 0x10000000;
1909             }
1910         }
1911         else
1912         {
1913             /* Use the session process and working set */
1914             CurrentProcess = HYDRA_PROCESS;
1915             WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1916 
1917             /* Make sure we don't have a recursive working set lock */
1918             if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1919                 (CurrentThread->OwnsSessionWorkingSetShared))
1920             {
1921                 /* Fail */
1922                 return STATUS_IN_PAGE_ERROR | 0x10000000;
1923             }
1924         }
1925 
1926         /* Acquire the working set lock */
1927         KeRaiseIrql(APC_LEVEL, &LockIrql);
1928         MiLockWorkingSet(CurrentThread, WorkingSet);
1929 
1930         /* Re-read PTE now that we own the lock */
1931         TempPte = *PointerPte;
1932         if (TempPte.u.Hard.Valid == 1)
1933         {
1934             /* Check if this was a write */
1935             if (MI_IS_WRITE_ACCESS(FaultCode))
1936             {
1937                 /* Was it to a read-only page that is not copy on write? */
1938                 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1939                 if (!(TempPte.u.Long & PTE_READWRITE) &&
1940                     !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1941                     !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1942                 {
1943                     /* Case not yet handled */
1944                     ASSERT(!IsSessionAddress);
1945 
1946                     /* Crash with distinguished bugcheck code */
1947                     KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1948                                  (ULONG_PTR)Address,
1949                                  TempPte.u.Long,
1950                                  (ULONG_PTR)TrapInformation,
1951                                  12);
1952                 }
1953             }
1954 
1955             /* Check for execution of non-executable memory */
1956             if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
1957                 !MI_IS_PAGE_EXECUTABLE(&TempPte))
1958             {
1959                 KeBugCheckEx(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY,
1960                              (ULONG_PTR)Address,
1961                              (ULONG_PTR)TempPte.u.Long,
1962                              (ULONG_PTR)TrapInformation,
1963                              2);
1964             }
1965 
1966             /* Check for read-only write in session space */
1967             if ((IsSessionAddress) &&
1968                 MI_IS_WRITE_ACCESS(FaultCode) &&
1969                 !MI_IS_PAGE_WRITEABLE(&TempPte))
1970             {
1971                 /* Sanity check */
1972                 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1973 
1974                 /* Was this COW? */
1975                 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1976                 {
1977                     /* Then this is not allowed */
1978                     KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1979                                  (ULONG_PTR)Address,
1980                                  (ULONG_PTR)TempPte.u.Long,
1981                                  (ULONG_PTR)TrapInformation,
1982                                  13);
1983                 }
1984 
1985                 /* Otherwise, handle COW */
1986                 ASSERT(FALSE);
1987             }
1988 
1989             /* Release the working set */
1990             MiUnlockWorkingSet(CurrentThread, WorkingSet);
1991             KeLowerIrql(LockIrql);
1992 
1993             /* Otherwise, the PDE was probably invalid, and all is good now */
1994             return STATUS_SUCCESS;
1995         }
1996 
1997         /* Check one kind of prototype PTE */
1998         if (TempPte.u.Soft.Prototype)
1999         {
2000             /* Make sure protected pool is on, and that this is a pool address */
2001             if ((MmProtectFreedNonPagedPool) &&
2002                 (((Address >= MmNonPagedPoolStart) &&
2003                   (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
2004                                      MmSizeOfNonPagedPoolInBytes))) ||
2005                  ((Address >= MmNonPagedPoolExpansionStart) &&
2006                   (Address < MmNonPagedPoolEnd))))
2007             {
2008                 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
2009                 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
2010                              (ULONG_PTR)Address,
2011                              FaultCode,
2012                              Mode,
2013                              4);
2014             }
2015 
2016             /* Get the prototype PTE! */
2017             ProtoPte = MiProtoPteToPte(&TempPte);
2018 
2019             /* Do we need to locate the prototype PTE in session space? */
2020             if ((IsSessionAddress) &&
2021                 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
2022             {
2023                 /* Yep, go find it as well as the VAD for it */
2024                 ProtoPte = MiCheckVirtualAddress(Address,
2025                                                  &ProtectionCode,
2026                                                  &Vad);
2027                 ASSERT(ProtoPte != NULL);
2028             }
2029         }
2030         else
2031         {
2032             /* We don't implement transition PTEs */
2033             ASSERT(TempPte.u.Soft.Transition == 0);
2034 
2035             /* Check for no-access PTE */
2036             if (TempPte.u.Soft.Protection == MM_NOACCESS)
2037             {
2038                 /* Bugcheck the system! */
2039                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
2040                              (ULONG_PTR)Address,
2041                              FaultCode,
2042                              (ULONG_PTR)TrapInformation,
2043                              1);
2044             }
2045 
2046             /* Check for no protecton at all */
2047             if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS)
2048             {
2049                 /* Bugcheck the system! */
2050                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
2051                              (ULONG_PTR)Address,
2052                              FaultCode,
2053                              (ULONG_PTR)TrapInformation,
2054                              0);
2055             }
2056         }
2057 
2058         /* Check for demand page */
2059         if (MI_IS_WRITE_ACCESS(FaultCode) &&
2060             !(ProtoPte) &&
2061             !(IsSessionAddress) &&
2062             !(TempPte.u.Hard.Valid))
2063         {
2064             /* Get the protection code */
2065             ASSERT(TempPte.u.Soft.Transition == 0);
2066             if (!(TempPte.u.Soft.Protection & MM_READWRITE))
2067             {
2068                 /* Bugcheck the system! */
2069                 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
2070                              (ULONG_PTR)Address,
2071                              TempPte.u.Long,
2072                              (ULONG_PTR)TrapInformation,
2073                              14);
2074             }
2075         }
2076 
2077         /* Now do the real fault handling */
2078         Status = MiDispatchFault(FaultCode,
2079                                  Address,
2080                                  PointerPte,
2081                                  ProtoPte,
2082                                  FALSE,
2083                                  CurrentProcess,
2084                                  TrapInformation,
2085                                  NULL);
2086 
2087         /* Release the working set */
2088         ASSERT(KeAreAllApcsDisabled() == TRUE);
2089         MiUnlockWorkingSet(CurrentThread, WorkingSet);
2090         KeLowerIrql(LockIrql);
2091 
2092         /* We are done! */
2093         DPRINT("Fault resolved with status: %lx\n", Status);
2094         return Status;
2095     }
2096 
2097     /* This is a user fault */
2098 UserFault:
2099     CurrentThread = PsGetCurrentThread();
2100     CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
2101 
2102     /* Lock the working set */
2103     MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
2104 
2105     ProtectionCode = MM_INVALID_PROTECTION;
2106 
2107 #if (_MI_PAGING_LEVELS == 4)
2108     /* Check if the PXE is valid */
2109     if (PointerPxe->u.Hard.Valid == 0)
2110     {
2111         /* Right now, we only handle scenarios where the PXE is totally empty */
2112         ASSERT(PointerPxe->u.Long == 0);
2113 
2114         /* This is only possible for user mode addresses! */
2115         ASSERT(PointerPte <= MiHighestUserPte);
2116 
2117         /* Check if we have a VAD */
2118         MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2119         if (ProtectionCode == MM_NOACCESS)
2120         {
2121             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2122             return STATUS_ACCESS_VIOLATION;
2123         }
2124 
2125         /* Resolve a demand zero fault */
2126         MiResolveDemandZeroFault(PointerPpe,
2127                                  PointerPxe,
2128                                  MM_EXECUTE_READWRITE,
2129                                  CurrentProcess,
2130                                  MM_NOIRQL);
2131 
2132         /* We should come back with a valid PXE */
2133         ASSERT(PointerPxe->u.Hard.Valid == 1);
2134     }
2135 #endif
2136 
2137 #if (_MI_PAGING_LEVELS >= 3)
2138     /* Check if the PPE is valid */
2139     if (PointerPpe->u.Hard.Valid == 0)
2140     {
2141         /* Right now, we only handle scenarios where the PPE is totally empty */
2142         ASSERT(PointerPpe->u.Long == 0);
2143 
2144         /* This is only possible for user mode addresses! */
2145         ASSERT(PointerPte <= MiHighestUserPte);
2146 
2147         /* Check if we have a VAD, unless we did this already */
2148         if (ProtectionCode == MM_INVALID_PROTECTION)
2149         {
2150             MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2151         }
2152 
2153         if (ProtectionCode == MM_NOACCESS)
2154         {
2155             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2156             return STATUS_ACCESS_VIOLATION;
2157         }
2158 
2159         /* Resolve a demand zero fault */
2160         MiResolveDemandZeroFault(PointerPde,
2161                                  PointerPpe,
2162                                  MM_EXECUTE_READWRITE,
2163                                  CurrentProcess,
2164                                  MM_NOIRQL);
2165 
2166         /* We should come back with a valid PPE */
2167         ASSERT(PointerPpe->u.Hard.Valid == 1);
2168     }
2169 #endif
2170 
2171     /* Check if the PDE is invalid */
2172     if (PointerPde->u.Hard.Valid == 0)
2173     {
2174         /* Right now, we only handle scenarios where the PDE is totally empty */
2175         ASSERT(PointerPde->u.Long == 0);
2176 
2177         /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2178 #if MI_TRACE_PFNS
2179         UserPdeFault = TRUE;
2180 #endif
2181         /* Check if we have a VAD, unless we did this already */
2182         if (ProtectionCode == MM_INVALID_PROTECTION)
2183         {
2184             MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2185         }
2186 
2187         if (ProtectionCode == MM_NOACCESS)
2188         {
2189 #if (_MI_PAGING_LEVELS == 2)
2190             /* Could be a page table for paged pool */
2191             MiCheckPdeForPagedPool(Address);
2192 #endif
2193             /* Has the code above changed anything -- is this now a valid PTE? */
2194             Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2195 
2196             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2197             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2198             return Status;
2199         }
2200 
2201         /* Resolve a demand zero fault */
2202         MiResolveDemandZeroFault(PointerPte,
2203                                  PointerPde,
2204                                  MM_EXECUTE_READWRITE,
2205                                  CurrentProcess,
2206                                  MM_NOIRQL);
2207 #if MI_TRACE_PFNS
2208         UserPdeFault = FALSE;
2209 #endif
2210         /* We should come back with APCs enabled, and with a valid PDE */
2211         ASSERT(KeAreAllApcsDisabled() == TRUE);
2212         ASSERT(PointerPde->u.Hard.Valid == 1);
2213     }
2214     else
2215     {
2216         /* Not yet implemented in ReactOS */
2217         ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
2218     }
2219 
2220     /* Now capture the PTE. */
2221     TempPte = *PointerPte;
2222 
2223     /* Check if the PTE is valid */
2224     if (TempPte.u.Hard.Valid)
2225     {
2226         /* Check if this is a write on a readonly PTE */
2227         if (MI_IS_WRITE_ACCESS(FaultCode))
2228         {
2229             /* Is this a copy on write PTE? */
2230             if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
2231             {
2232                 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
2233                 PMMPFN Pfn1;
2234                 ProtectionCode = TempPte.u.Soft.Protection;
2235 
2236                 LockIrql = MiAcquirePfnLock();
2237 
2238                 ASSERT(MmAvailablePages > 0);
2239 
2240                 MI_SET_USAGE(MI_USAGE_COW);
2241                 MI_SET_PROCESS(CurrentProcess);
2242 
2243                 /* Allocate a new page and copy it */
2244                 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
2245                 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
2246 
2247                 MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
2248 
2249                 /* Dereference whatever this PTE is referencing */
2250                 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
2251                 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
2252                 ASSERT(!MI_IS_PFN_DELETED(Pfn1));
2253                 ProtoPte = Pfn1->PteAddress;
2254                 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
2255 
2256                 /* And make a new shiny one with our page */
2257                 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
2258 
2259                 MiReleasePfnLock(LockIrql);
2260 
2261                 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
2262                 TempPte.u.Hard.Write = 1;
2263                 TempPte.u.Hard.CopyOnWrite = 0;
2264 
2265                 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2266 
2267                 /* We can now add it to our working set */
2268                 MiInsertInWorkingSetList(&CurrentProcess->Vm, Address, ProtectionCode);
2269 
2270 
2271                 /* Return the status */
2272                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2273                 return STATUS_PAGE_FAULT_COPY_ON_WRITE;
2274             }
2275 
2276             /* Is this a read-only PTE? */
2277             if (!MI_IS_PAGE_WRITEABLE(&TempPte))
2278             {
2279                 /* Return the status */
2280                 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2281                 return STATUS_ACCESS_VIOLATION;
2282             }
2283         }
2284 
2285         /* Check for execution of non-executable memory */
2286         if (MI_IS_INSTRUCTION_FETCH(FaultCode) &&
2287             !MI_IS_PAGE_EXECUTABLE(&TempPte))
2288         {
2289             /* Return the status */
2290             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2291             return STATUS_ACCESS_VIOLATION;
2292         }
2293 
2294         /* The fault has already been resolved by a different thread */
2295         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2296         return STATUS_SUCCESS;
2297     }
2298 
2299     /* Quick check for demand-zero */
2300     if ((TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) ||
2301         (TempPte.u.Long == (MM_EXECUTE_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)))
2302     {
2303         /* Resolve the fault */
2304         MiResolveDemandZeroFault(Address,
2305                                  PointerPte,
2306                                  TempPte.u.Soft.Protection,
2307                                  CurrentProcess,
2308                                  MM_NOIRQL);
2309 
2310         /* Return the status */
2311         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2312         return STATUS_PAGE_FAULT_DEMAND_ZERO;
2313     }
2314 
2315     /* Check for zero PTE */
2316     if (TempPte.u.Long == 0)
2317     {
2318         /* Check if this address range belongs to a valid allocation (VAD) */
2319         ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2320         if (ProtectionCode == MM_NOACCESS)
2321         {
2322 #if (_MI_PAGING_LEVELS == 2)
2323             /* Could be a page table for paged pool */
2324             MiCheckPdeForPagedPool(Address);
2325 #endif
2326             /* Has the code above changed anything -- is this now a valid PTE? */
2327             Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2328 
2329             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2330             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2331             return Status;
2332         }
2333 
2334         /*
2335          * Check if this is a real user-mode address or actually a kernel-mode
2336          * page table for a user mode address
2337          */
2338         if (Address <= MM_HIGHEST_USER_ADDRESS)
2339         {
2340             /* Add an additional page table reference */
2341             MiIncrementPageTableReferences(Address);
2342         }
2343 
2344         /* Is this a guard page? */
2345         if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
2346         {
2347             /* The VAD protection cannot be MM_DECOMMIT! */
2348             ASSERT(ProtectionCode != MM_DECOMMIT);
2349 
2350             /* Remove the bit */
2351             TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
2352             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2353 
2354             /* Not supported */
2355             ASSERT(ProtoPte == NULL);
2356             ASSERT(CurrentThread->ApcNeeded == 0);
2357 
2358             /* Drop the working set lock */
2359             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2360             ASSERT(KeGetCurrentIrql() == OldIrql);
2361 
2362             /* Handle stack expansion */
2363             return MiCheckForUserStackOverflow(Address, TrapInformation);
2364         }
2365 
2366         /* Did we get a prototype PTE back? */
2367         if (!ProtoPte)
2368         {
2369             /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2370             if (PointerPde == MiAddressToPde(PTE_BASE))
2371             {
2372                 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2373 #ifdef _M_ARM
2374                 _WARN("This is probably completely broken!");
2375                 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
2376 #else
2377                 MI_WRITE_INVALID_PDE(PointerPte, DemandZeroPde);
2378 #endif
2379             }
2380             else
2381             {
2382                 /* No, create a new PTE. First, write the protection */
2383                 TempPte.u.Soft.Protection = ProtectionCode;
2384                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2385             }
2386             ProtectionCode = PointerPte->u.Soft.Protection;
2387 
2388             /* Lock the PFN database since we're going to grab a page */
2389             OldIrql = MiAcquirePfnLock();
2390 
2391             /* Make sure we have enough pages */
2392             ASSERT(MmAvailablePages >= 32);
2393 
2394             /* Try to get a zero page */
2395             MI_SET_USAGE(MI_USAGE_PEB_TEB);
2396             MI_SET_PROCESS2(CurrentProcess->ImageFileName);
2397             Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
2398             PageFrameIndex = MiRemoveZeroPageSafe(Color);
2399             if (!PageFrameIndex)
2400             {
2401                 /* Grab a page out of there. Later we should grab a colored zero page */
2402                 PageFrameIndex = MiRemoveAnyPage(Color);
2403                 ASSERT(PageFrameIndex);
2404 
2405                 /* Release the lock since we need to do some zeroing */
2406                 MiReleasePfnLock(OldIrql);
2407 
2408                 /* Zero out the page, since it's for user-mode */
2409                 MiZeroPfn(PageFrameIndex);
2410 
2411                 /* Grab the lock again so we can initialize the PFN entry */
2412                 OldIrql = MiAcquirePfnLock();
2413             }
2414 
2415             /* Initialize the PFN entry now */
2416             MiInitializePfn(PageFrameIndex, PointerPte, 1);
2417 
2418             /* And be done with the lock */
2419             MiReleasePfnLock(OldIrql);
2420 
2421             /* Increment the count of pages in the process */
2422             CurrentProcess->NumberOfPrivatePages++;
2423 
2424             /* One more demand-zero fault */
2425             InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
2426 
2427             /* Fault on user PDE, or fault on user PTE? */
2428             if (PointerPte <= MiHighestUserPte)
2429             {
2430                 /* User fault, build a user PTE */
2431                 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2432                                           PointerPte,
2433                                           PointerPte->u.Soft.Protection,
2434                                           PageFrameIndex);
2435             }
2436             else
2437             {
2438                 /* This is a user-mode PDE, create a kernel PTE for it */
2439                 MI_MAKE_HARDWARE_PTE(&TempPte,
2440                                      PointerPte,
2441                                      PointerPte->u.Soft.Protection,
2442                                      PageFrameIndex);
2443             }
2444 
2445             /* Write the dirty bit for writeable pages */
2446             if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2447 
2448             /* And now write down the PTE, making the address valid */
2449             MI_WRITE_VALID_PTE(PointerPte, TempPte);
2450             Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2451             ASSERT(Pfn1->u1.Event == NULL);
2452 
2453             /* We can now insert it into the working set */
2454             MiInsertInWorkingSetList(&CurrentProcess->Vm, Address, ProtectionCode);
2455 
2456             /* Demand zero */
2457             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2458             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2459             return STATUS_PAGE_FAULT_DEMAND_ZERO;
2460         }
2461 
2462         /* We should have a valid protection here */
2463         ASSERT(ProtectionCode != 0x100);
2464 
2465         /* Write the prototype PTE */
2466         TempPte = PrototypePte;
2467         TempPte.u.Soft.Protection = ProtectionCode;
2468         ASSERT(TempPte.u.Long != 0);
2469         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2470     }
2471     else
2472     {
2473         /* Get the protection code and check if this is a proto PTE */
2474         ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2475         if (TempPte.u.Soft.Prototype)
2476         {
2477             /* Do we need to go find the real PTE? */
2478             if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2479             {
2480                 /* Get the prototype pte and VAD for it */
2481                 ProtoPte = MiCheckVirtualAddress(Address,
2482                                                  &ProtectionCode,
2483                                                  &Vad);
2484                 if (!ProtoPte)
2485                 {
2486                     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2487                     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2488                     return STATUS_ACCESS_VIOLATION;
2489                 }
2490             }
2491             else
2492             {
2493                 /* Get the prototype PTE! */
2494                 ProtoPte = MiProtoPteToPte(&TempPte);
2495 
2496                 /* Is it read-only */
2497                 if (TempPte.u.Proto.ReadOnly)
2498                 {
2499                     /* Set read-only code */
2500                     ProtectionCode = MM_READONLY;
2501                 }
2502                 else
2503                 {
2504                     /* Set unknown protection */
2505                     ProtectionCode = 0x100;
2506                     ASSERT(CurrentProcess->CloneRoot != NULL);
2507                 }
2508             }
2509         }
2510     }
2511 
2512     /* Do we have a valid protection code? */
2513     if (ProtectionCode != 0x100)
2514     {
2515         /* Run a software access check first, including to detect guard pages */
2516         Status = MiAccessCheck(PointerPte,
2517                                !MI_IS_NOT_PRESENT_FAULT(FaultCode),
2518                                Mode,
2519                                ProtectionCode,
2520                                TrapInformation,
2521                                FALSE);
2522         if (Status != STATUS_SUCCESS)
2523         {
2524             /* Not supported */
2525             ASSERT(CurrentThread->ApcNeeded == 0);
2526 
2527             /* Drop the working set lock */
2528             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2529             ASSERT(KeGetCurrentIrql() == OldIrql);
2530 
2531             /* Did we hit a guard page? */
2532             if (Status == STATUS_GUARD_PAGE_VIOLATION)
2533             {
2534                 /* Handle stack expansion */
2535                 return MiCheckForUserStackOverflow(Address, TrapInformation);
2536             }
2537 
2538             /* Otherwise, fail back to the caller directly */
2539             return Status;
2540         }
2541     }
2542 
2543     /* Dispatch the fault */
2544     Status = MiDispatchFault(FaultCode,
2545                              Address,
2546                              PointerPte,
2547                              ProtoPte,
2548                              FALSE,
2549                              CurrentProcess,
2550                              TrapInformation,
2551                              Vad);
2552 
2553     /* Return the status */
2554     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2555     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2556     return Status;
2557 }
2558 
2559 NTSTATUS
2560 NTAPI
2561 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2562 {
2563     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2564     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2565 
2566     *ExecuteOptions = 0;
2567 
2568     if (CurrentProcess->Flags.ExecuteDisable)
2569     {
2570         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2571     }
2572 
2573     if (CurrentProcess->Flags.ExecuteEnable)
2574     {
2575         *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2576     }
2577 
2578     if (CurrentProcess->Flags.DisableThunkEmulation)
2579     {
2580         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2581     }
2582 
2583     if (CurrentProcess->Flags.Permanent)
2584     {
2585         *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2586     }
2587 
2588     if (CurrentProcess->Flags.ExecuteDispatchEnable)
2589     {
2590         *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2591     }
2592 
2593     if (CurrentProcess->Flags.ImageDispatchEnable)
2594     {
2595         *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2596     }
2597 
2598     return STATUS_SUCCESS;
2599 }
2600 
2601 NTSTATUS
2602 NTAPI
2603 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2604 {
2605     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2606     KLOCK_QUEUE_HANDLE ProcessLock;
2607     NTSTATUS Status = STATUS_ACCESS_DENIED;
2608     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2609 
2610     /* Only accept valid flags */
2611     if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2612     {
2613         /* Fail */
2614         DPRINT1("Invalid no-execute options\n");
2615         return STATUS_INVALID_PARAMETER;
2616     }
2617 
2618     /* Change the NX state in the process lock */
2619     KiAcquireProcessLockRaiseToSynch(CurrentProcess, &ProcessLock);
2620 
2621     /* Don't change anything if the permanent flag was set */
2622     if (!CurrentProcess->Flags.Permanent)
2623     {
2624         /* Start by assuming it's not disabled */
2625         CurrentProcess->Flags.ExecuteDisable = FALSE;
2626 
2627         /* Now process each flag and turn the equivalent bit on */
2628         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2629         {
2630             CurrentProcess->Flags.ExecuteDisable = TRUE;
2631         }
2632         if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2633         {
2634             CurrentProcess->Flags.ExecuteEnable = TRUE;
2635         }
2636         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2637         {
2638             CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2639         }
2640         if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2641         {
2642             CurrentProcess->Flags.Permanent = TRUE;
2643         }
2644         if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2645         {
2646             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2647         }
2648         if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2649         {
2650             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2651         }
2652 
2653         /* These are turned on by default if no-execution is also eanbled */
2654         if (CurrentProcess->Flags.ExecuteEnable)
2655         {
2656             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2657             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2658         }
2659 
2660         /* All good */
2661         Status = STATUS_SUCCESS;
2662     }
2663 
2664     /* Release the lock and return status */
2665     KiReleaseProcessLock(&ProcessLock);
2666     return Status;
2667 }
2668 
2669 /* EOF */
2670