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