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