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