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