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