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