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