[SHELL32] Remove 2 redundant initializations
[reactos.git] / ntoskrnl / mm / ARM3 / pagfault.c
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
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
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
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
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
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
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
479 MiCheckPdeForPagedPool(IN PVOID Address)
480 {
481 return STATUS_ACCESS_VIOLATION;
482 }
483 #endif
484
485 VOID
486 NTAPI
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
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
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
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
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
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
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
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
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
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
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 */