[NTOS:MM]
[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 {
1370 /* This fault is not valid, printf out some debugging help */
1371 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1372 Address,
1373 OldIrql);
1374 if (TrapInformation)
1375 {
1376 PKTRAP_FRAME TrapFrame = TrapInformation;
1377 #ifdef _M_IX86
1378 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1379 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1380 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1381 #elif defined(_M_AMD64)
1382 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1383 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1384 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1385 #endif
1386 }
1387
1388 /* Tell the trap handler to fail */
1389 return STATUS_IN_PAGE_ERROR | 0x10000000;
1390 }
1391
1392 /* Not yet implemented in ReactOS */
1393 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1394 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1395
1396 /* Check if this was a write */
1397 if (StoreInstruction)
1398 {
1399 /* Was it to a read-only page? */
1400 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1401 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1402 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1403 {
1404 /* Crash with distinguished bugcheck code */
1405 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1406 (ULONG_PTR)Address,
1407 PointerPte->u.Long,
1408 (ULONG_PTR)TrapInformation,
1409 10);
1410 }
1411 }
1412
1413 /* Nothing is actually wrong */
1414 DPRINT1("Fault at IRQL1 is ok\n");
1415 return STATUS_SUCCESS;
1416 }
1417
1418 /* Check for kernel fault address */
1419 if (Address >= MmSystemRangeStart)
1420 {
1421 /* Bail out, if the fault came from user mode */
1422 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1423
1424 #if (_MI_PAGING_LEVELS == 4)
1425 /* AMD64 system, check if PXE is invalid */
1426 if (PointerPxe->u.Hard.Valid == 0)
1427 {
1428 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1429 (ULONG_PTR)Address,
1430 StoreInstruction,
1431 (ULONG_PTR)TrapInformation,
1432 7);
1433 }
1434 #endif
1435 #if (_MI_PAGING_LEVELS == 4)
1436 /* PAE/AMD64 system, check if PPE is invalid */
1437 if (PointerPpe->u.Hard.Valid == 0)
1438 {
1439 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1440 (ULONG_PTR)Address,
1441 StoreInstruction,
1442 (ULONG_PTR)TrapInformation,
1443 5);
1444 }
1445 #endif
1446 #if (_MI_PAGING_LEVELS == 2)
1447 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1448 MiCheckPdeForPagedPool(Address);
1449 #endif
1450
1451 /* Check if the PDE is invalid */
1452 if (PointerPde->u.Hard.Valid == 0)
1453 {
1454 /* PDE (still) not valid, kill the system */
1455 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1456 (ULONG_PTR)Address,
1457 StoreInstruction,
1458 (ULONG_PTR)TrapInformation,
1459 2);
1460 }
1461
1462 /* Not handling session faults yet */
1463 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1464
1465 /* The PDE is valid, so read the PTE */
1466 TempPte = *PointerPte;
1467 if (TempPte.u.Hard.Valid == 1)
1468 {
1469 /* Check if this was system space or session space */
1470 if (!IsSessionAddress)
1471 {
1472 /* Check if the PTE is still valid under PFN lock */
1473 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1474 TempPte = *PointerPte;
1475 if (TempPte.u.Hard.Valid)
1476 {
1477 /* Check if this was a write */
1478 if (StoreInstruction)
1479 {
1480 /* Was it to a read-only page? */
1481 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1482 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1483 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1484 {
1485 /* Crash with distinguished bugcheck code */
1486 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1487 (ULONG_PTR)Address,
1488 PointerPte->u.Long,
1489 (ULONG_PTR)TrapInformation,
1490 11);
1491 }
1492 }
1493 }
1494
1495 /* Release PFN lock and return all good */
1496 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1497 return STATUS_SUCCESS;
1498 }
1499 }
1500 #if (_MI_PAGING_LEVELS == 2)
1501 /* Check if this was a session PTE that needs to remap the session PDE */
1502 if (MI_IS_SESSION_PTE(Address))
1503 {
1504 /* Do the remapping */
1505 Status = MiCheckPdeForSessionSpace(Address);
1506 if (!NT_SUCCESS(Status))
1507 {
1508 /* It failed, this address is invalid */
1509 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1510 (ULONG_PTR)Address,
1511 StoreInstruction,
1512 (ULONG_PTR)TrapInformation,
1513 6);
1514 }
1515 }
1516 #else
1517
1518 _WARN("Session space stuff is not implemented yet!")
1519
1520 #endif
1521
1522 /* Check for a fault on the page table or hyperspace */
1523 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1524 {
1525 #if (_MI_PAGING_LEVELS < 3)
1526 /* Windows does this check but I don't understand why -- it's done above! */
1527 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1528 #endif
1529 /* Handle this as a user mode fault */
1530 goto UserFault;
1531 }
1532
1533 /* Get the current thread */
1534 CurrentThread = PsGetCurrentThread();
1535
1536 /* What kind of address is this */
1537 if (!IsSessionAddress)
1538 {
1539 /* Use the system working set */
1540 WorkingSet = &MmSystemCacheWs;
1541 CurrentProcess = NULL;
1542
1543 /* Make sure we don't have a recursive working set lock */
1544 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1545 (CurrentThread->OwnsProcessWorkingSetShared) ||
1546 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1547 (CurrentThread->OwnsSystemWorkingSetShared) ||
1548 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1549 (CurrentThread->OwnsSessionWorkingSetShared))
1550 {
1551 /* Fail */
1552 return STATUS_IN_PAGE_ERROR | 0x10000000;
1553 }
1554 }
1555 else
1556 {
1557 /* Use the session process and working set */
1558 CurrentProcess = HYDRA_PROCESS;
1559 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1560
1561 /* Make sure we don't have a recursive working set lock */
1562 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1563 (CurrentThread->OwnsSessionWorkingSetShared))
1564 {
1565 /* Fail */
1566 return STATUS_IN_PAGE_ERROR | 0x10000000;
1567 }
1568 }
1569
1570 /* Acquire the working set lock */
1571 KeRaiseIrql(APC_LEVEL, &LockIrql);
1572 MiLockWorkingSet(CurrentThread, WorkingSet);
1573
1574 /* Re-read PTE now that we own the lock */
1575 TempPte = *PointerPte;
1576 if (TempPte.u.Hard.Valid == 1)
1577 {
1578 /* Check if this was a write */
1579 if (StoreInstruction)
1580 {
1581 /* Was it to a read-only page that is not copy on write? */
1582 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1583 if (!(TempPte.u.Long & PTE_READWRITE) &&
1584 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1585 !(TempPte.u.Hard.CopyOnWrite))
1586 {
1587 /* Case not yet handled */
1588 ASSERT(!IsSessionAddress);
1589
1590 /* Crash with distinguished bugcheck code */
1591 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1592 (ULONG_PTR)Address,
1593 TempPte.u.Long,
1594 (ULONG_PTR)TrapInformation,
1595 12);
1596 }
1597 }
1598
1599 /* Check for read-only write in session space */
1600 if ((IsSessionAddress) &&
1601 (StoreInstruction) &&
1602 !(TempPte.u.Hard.Write))
1603 {
1604 /* Sanity check */
1605 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1606
1607 /* Was this COW? */
1608 if (TempPte.u.Hard.CopyOnWrite == 0)
1609 {
1610 /* Then this is not allowed */
1611 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1612 (ULONG_PTR)Address,
1613 (ULONG_PTR)TempPte.u.Long,
1614 (ULONG_PTR)TrapInformation,
1615 13);
1616 }
1617
1618 /* Otherwise, handle COW */
1619 ASSERT(FALSE);
1620 }
1621
1622 /* Release the working set */
1623 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1624 KeLowerIrql(LockIrql);
1625
1626 /* Otherwise, the PDE was probably invalid, and all is good now */
1627 return STATUS_SUCCESS;
1628 }
1629
1630 /* Check one kind of prototype PTE */
1631 if (TempPte.u.Soft.Prototype)
1632 {
1633 /* Make sure protected pool is on, and that this is a pool address */
1634 if ((MmProtectFreedNonPagedPool) &&
1635 (((Address >= MmNonPagedPoolStart) &&
1636 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1637 MmSizeOfNonPagedPoolInBytes))) ||
1638 ((Address >= MmNonPagedPoolExpansionStart) &&
1639 (Address < MmNonPagedPoolEnd))))
1640 {
1641 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1642 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1643 (ULONG_PTR)Address,
1644 StoreInstruction,
1645 Mode,
1646 4);
1647 }
1648
1649 /* Get the prototype PTE! */
1650 ProtoPte = MiProtoPteToPte(&TempPte);
1651
1652 /* Do we need to locate the prototype PTE in session space? */
1653 if ((IsSessionAddress) &&
1654 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1655 {
1656 /* Yep, go find it as well as the VAD for it */
1657 ProtoPte = MiCheckVirtualAddress(Address,
1658 &ProtectionCode,
1659 &Vad);
1660 ASSERT(ProtoPte != NULL);
1661 }
1662 }
1663 else
1664 {
1665 /* We don't implement transition PTEs */
1666 ASSERT(TempPte.u.Soft.Transition == 0);
1667
1668 /* Check for no-access PTE */
1669 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1670 {
1671 /* Bugcheck the system! */
1672 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1673 (ULONG_PTR)Address,
1674 StoreInstruction,
1675 (ULONG_PTR)TrapInformation,
1676 1);
1677 }
1678 }
1679
1680 /* Check for demand page */
1681 if ((StoreInstruction) &&
1682 !(ProtoPte) &&
1683 !(IsSessionAddress) &&
1684 !(TempPte.u.Hard.Valid))
1685 {
1686 /* Get the protection code */
1687 ASSERT(TempPte.u.Soft.Transition == 0);
1688 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1689 {
1690 /* Bugcheck the system! */
1691 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1692 (ULONG_PTR)Address,
1693 TempPte.u.Long,
1694 (ULONG_PTR)TrapInformation,
1695 14);
1696 }
1697 }
1698
1699 /* Now do the real fault handling */
1700 Status = MiDispatchFault(StoreInstruction,
1701 Address,
1702 PointerPte,
1703 ProtoPte,
1704 FALSE,
1705 CurrentProcess,
1706 TrapInformation,
1707 NULL);
1708
1709 /* Release the working set */
1710 ASSERT(KeAreAllApcsDisabled() == TRUE);
1711 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1712 KeLowerIrql(LockIrql);
1713
1714 /* We are done! */
1715 DPRINT("Fault resolved with status: %lx\n", Status);
1716 return Status;
1717 }
1718
1719 /* This is a user fault */
1720 UserFault:
1721 CurrentThread = PsGetCurrentThread();
1722 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1723
1724 /* Lock the working set */
1725 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1726
1727 #if (_MI_PAGING_LEVELS == 4)
1728 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1729 // also this is missing the page count increment
1730 /* Check if the PXE is valid */
1731 if (PointerPxe->u.Hard.Valid == 0)
1732 {
1733 /* Right now, we only handle scenarios where the PXE is totally empty */
1734 ASSERT(PointerPxe->u.Long == 0);
1735 #if 0
1736 /* Resolve a demand zero fault */
1737 Status = MiResolveDemandZeroFault(PointerPpe,
1738 MM_READWRITE,
1739 CurrentProcess,
1740 MM_NOIRQL);
1741 #endif
1742 /* We should come back with a valid PXE */
1743 ASSERT(PointerPxe->u.Hard.Valid == 1);
1744 }
1745 #endif
1746
1747 #if (_MI_PAGING_LEVELS >= 3)
1748 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1749 // also this is missing the page count increment
1750 /* Check if the PPE is valid */
1751 if (PointerPpe->u.Hard.Valid == 0)
1752 {
1753 /* Right now, we only handle scenarios where the PPE is totally empty */
1754 ASSERT(PointerPpe->u.Long == 0);
1755 #if 0
1756 /* Resolve a demand zero fault */
1757 Status = MiResolveDemandZeroFault(PointerPde,
1758 MM_READWRITE,
1759 CurrentProcess,
1760 MM_NOIRQL);
1761 #endif
1762 /* We should come back with a valid PPE */
1763 ASSERT(PointerPpe->u.Hard.Valid == 1);
1764 }
1765 #endif
1766
1767 /* Check if the PDE is valid */
1768 if (PointerPde->u.Hard.Valid == 0)
1769 {
1770 /* Right now, we only handle scenarios where the PDE is totally empty */
1771 ASSERT(PointerPde->u.Long == 0);
1772
1773 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1774 #if MI_TRACE_PFNS
1775 UserPdeFault = TRUE;
1776 #endif
1777 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1778 if (ProtectionCode == MM_NOACCESS)
1779 {
1780 #if (_MI_PAGING_LEVELS == 2)
1781 /* Could be a page table for paged pool */
1782 MiCheckPdeForPagedPool(Address);
1783 #endif
1784 /* Has the code above changed anything -- is this now a valid PTE? */
1785 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1786
1787 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1788 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1789 return Status;
1790 }
1791
1792 /* Write a demand-zero PDE */
1793 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1794
1795 /* Dispatch the fault */
1796 Status = MiDispatchFault(TRUE,
1797 PointerPte,
1798 PointerPde,
1799 NULL,
1800 FALSE,
1801 PsGetCurrentProcess(),
1802 TrapInformation,
1803 NULL);
1804 #if MI_TRACE_PFNS
1805 UserPdeFault = FALSE;
1806 #endif
1807 /* We should come back with APCs enabled, and with a valid PDE */
1808 ASSERT(KeAreAllApcsDisabled() == TRUE);
1809 ASSERT(PointerPde->u.Hard.Valid == 1);
1810 }
1811 else
1812 {
1813 /* Not yet implemented in ReactOS */
1814 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1815 }
1816
1817 /* Now capture the PTE. Ignore virtual faults for now */
1818 TempPte = *PointerPte;
1819 ASSERT(TempPte.u.Hard.Valid == 0);
1820
1821 /* Quick check for demand-zero */
1822 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1823 {
1824 /* Resolve the fault */
1825 MiResolveDemandZeroFault(Address,
1826 PointerPte,
1827 CurrentProcess,
1828 MM_NOIRQL);
1829
1830 /* Return the status */
1831 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1832 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1833 }
1834
1835 /* Check for zero PTE */
1836 if (TempPte.u.Long == 0)
1837 {
1838 /* Check if this address range belongs to a valid allocation (VAD) */
1839 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1840 if (ProtectionCode == MM_NOACCESS)
1841 {
1842 #if (_MI_PAGING_LEVELS == 2)
1843 /* Could be a page table for paged pool */
1844 MiCheckPdeForPagedPool(Address);
1845 #endif
1846 /* Has the code above changed anything -- is this now a valid PTE? */
1847 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1848
1849 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1850 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1851 return Status;
1852 }
1853
1854 /*
1855 * Check if this is a real user-mode address or actually a kernel-mode
1856 * page table for a user mode address
1857 */
1858 if (Address <= MM_HIGHEST_USER_ADDRESS)
1859 {
1860 /* Add an additional page table reference */
1861 MiIncrementPageTableReferences(Address);
1862 }
1863
1864 /* Is this a guard page? */
1865 if (ProtectionCode & MM_DECOMMIT)
1866 {
1867 /* Remove the bit */
1868 PointerPte->u.Soft.Protection = ProtectionCode & ~MM_DECOMMIT;
1869
1870 /* Not supported */
1871 ASSERT(ProtoPte == NULL);
1872 ASSERT(CurrentThread->ApcNeeded == 0);
1873
1874 /* Drop the working set lock */
1875 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1876 ASSERT(KeGetCurrentIrql() == OldIrql);
1877
1878 /* Handle stack expansion */
1879 return MiCheckForUserStackOverflow(Address, TrapInformation);
1880 }
1881
1882 /* Did we get a prototype PTE back? */
1883 if (!ProtoPte)
1884 {
1885 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1886 if (PointerPde == MiAddressToPde(PTE_BASE))
1887 {
1888 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1889 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1890 }
1891 else
1892 {
1893 /* No, create a new PTE. First, write the protection */
1894 PointerPte->u.Soft.Protection = ProtectionCode;
1895 }
1896
1897 /* Lock the PFN database since we're going to grab a page */
1898 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1899
1900 /* Make sure we have enough pages */
1901 ASSERT(MmAvailablePages >= 32);
1902
1903 /* Try to get a zero page */
1904 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1905 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1906 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1907 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1908 if (!PageFrameIndex)
1909 {
1910 /* Grab a page out of there. Later we should grab a colored zero page */
1911 PageFrameIndex = MiRemoveAnyPage(Color);
1912 ASSERT(PageFrameIndex);
1913
1914 /* Release the lock since we need to do some zeroing */
1915 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1916
1917 /* Zero out the page, since it's for user-mode */
1918 MiZeroPfn(PageFrameIndex);
1919
1920 /* Grab the lock again so we can initialize the PFN entry */
1921 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1922 }
1923
1924 /* Initialize the PFN entry now */
1925 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1926
1927 /* And we're done with the lock */
1928 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1929
1930 /* Increment the count of pages in the process */
1931 CurrentProcess->NumberOfPrivatePages++;
1932
1933 /* One more demand-zero fault */
1934 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1935
1936 /* Fault on user PDE, or fault on user PTE? */
1937 if (PointerPte <= MiHighestUserPte)
1938 {
1939 /* User fault, build a user PTE */
1940 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1941 PointerPte,
1942 PointerPte->u.Soft.Protection,
1943 PageFrameIndex);
1944 }
1945 else
1946 {
1947 /* This is a user-mode PDE, create a kernel PTE for it */
1948 MI_MAKE_HARDWARE_PTE(&TempPte,
1949 PointerPte,
1950 PointerPte->u.Soft.Protection,
1951 PageFrameIndex);
1952 }
1953
1954 /* Write the dirty bit for writeable pages */
1955 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1956
1957 /* And now write down the PTE, making the address valid */
1958 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1959 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1960 ASSERT(Pfn1->u1.Event == NULL);
1961
1962 /* Demand zero */
1963 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1964 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1965 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1966 }
1967
1968 /* We should have a valid protection here */
1969 ASSERT(ProtectionCode != 0x100);
1970
1971 /* Write the prototype PTE */
1972 TempPte = PrototypePte;
1973 TempPte.u.Soft.Protection = ProtectionCode;
1974 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1975 }
1976 else
1977 {
1978 /* Get the protection code and check if this is a proto PTE */
1979 ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
1980 if (TempPte.u.Soft.Prototype)
1981 {
1982 /* Do we need to go find the real PTE? */
1983 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1984 {
1985 /* Get the prototype pte and VAD for it */
1986 ProtoPte = MiCheckVirtualAddress(Address,
1987 &ProtectionCode,
1988 &Vad);
1989 if (!ProtoPte)
1990 {
1991 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1992 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1993 return STATUS_ACCESS_VIOLATION;
1994 }
1995 }
1996 else
1997 {
1998 /* Get the prototype PTE! */
1999 ProtoPte = MiProtoPteToPte(&TempPte);
2000
2001 /* Is it read-only */
2002 if (TempPte.u.Proto.ReadOnly)
2003 {
2004 /* Set read-only code */
2005 ProtectionCode = MM_READONLY;
2006 }
2007 else
2008 {
2009 /* Set unknown protection */
2010 ProtectionCode = 0x100;
2011 ASSERT(CurrentProcess->CloneRoot != NULL);
2012 }
2013 }
2014 }
2015 }
2016
2017 /* Do we have a valid protection code? */
2018 if (ProtectionCode != 0x100)
2019 {
2020 /* Run a software access check first, including to detect guard pages */
2021 Status = MiAccessCheck(PointerPte,
2022 StoreInstruction,
2023 Mode,
2024 ProtectionCode,
2025 TrapInformation,
2026 FALSE);
2027 if (Status != STATUS_SUCCESS)
2028 {
2029 /* Not supported */
2030 ASSERT(CurrentThread->ApcNeeded == 0);
2031
2032 /* Drop the working set lock */
2033 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2034 ASSERT(KeGetCurrentIrql() == OldIrql);
2035
2036 /* Did we hit a guard page? */
2037 if (Status == STATUS_GUARD_PAGE_VIOLATION)
2038 {
2039 /* Handle stack expansion */
2040 return MiCheckForUserStackOverflow(Address, TrapInformation);
2041 }
2042
2043 /* Otherwise, fail back to the caller directly */
2044 return Status;
2045 }
2046 }
2047
2048 /* Dispatch the fault */
2049 Status = MiDispatchFault(StoreInstruction,
2050 Address,
2051 PointerPte,
2052 ProtoPte,
2053 FALSE,
2054 CurrentProcess,
2055 TrapInformation,
2056 Vad);
2057
2058 /* Return the status */
2059 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2060 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2061 return Status;
2062 }
2063
2064 NTSTATUS
2065 NTAPI
2066 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2067 {
2068 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2069 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2070
2071 *ExecuteOptions = 0;
2072
2073 if (CurrentProcess->Flags.ExecuteDisable)
2074 {
2075 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2076 }
2077
2078 if (CurrentProcess->Flags.ExecuteEnable)
2079 {
2080 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2081 }
2082
2083 if (CurrentProcess->Flags.DisableThunkEmulation)
2084 {
2085 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2086 }
2087
2088 if (CurrentProcess->Flags.Permanent)
2089 {
2090 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2091 }
2092
2093 if (CurrentProcess->Flags.ExecuteDispatchEnable)
2094 {
2095 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2096 }
2097
2098 if (CurrentProcess->Flags.ImageDispatchEnable)
2099 {
2100 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2101 }
2102
2103 return STATUS_SUCCESS;
2104 }
2105
2106 NTSTATUS
2107 NTAPI
2108 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2109 {
2110 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2111 KLOCK_QUEUE_HANDLE ProcessLock;
2112 NTSTATUS Status = STATUS_ACCESS_DENIED;
2113 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2114
2115 /* Only accept valid flags */
2116 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2117 {
2118 /* Fail */
2119 DPRINT1("Invalid no-execute options\n");
2120 return STATUS_INVALID_PARAMETER;
2121 }
2122
2123 /* Change the NX state in the process lock */
2124 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
2125
2126 /* Don't change anything if the permanent flag was set */
2127 if (!CurrentProcess->Flags.Permanent)
2128 {
2129 /* Start by assuming it's not disabled */
2130 CurrentProcess->Flags.ExecuteDisable = FALSE;
2131
2132 /* Now process each flag and turn the equivalent bit on */
2133 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2134 {
2135 CurrentProcess->Flags.ExecuteDisable = TRUE;
2136 }
2137 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2138 {
2139 CurrentProcess->Flags.ExecuteEnable = TRUE;
2140 }
2141 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2142 {
2143 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2144 }
2145 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2146 {
2147 CurrentProcess->Flags.Permanent = TRUE;
2148 }
2149 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2150 {
2151 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2152 }
2153 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2154 {
2155 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2156 }
2157
2158 /* These are turned on by default if no-execution is also eanbled */
2159 if (CurrentProcess->Flags.ExecuteEnable)
2160 {
2161 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2162 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2163 }
2164
2165 /* All good */
2166 Status = STATUS_SUCCESS;
2167 }
2168
2169 /* Release the lock and return status */
2170 KiReleaseProcessLock(&ProcessLock);
2171 return Status;
2172 }
2173
2174 /* EOF */