50304dad89637530ec3fcfb815ce0b5b2446dc8f
[reactos.git] / ntoskrnl / mm / ARM3 / pagfault.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #line 15 "ARMĀ³::PAGFAULT"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 /* GLOBALS ********************************************************************/
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 PMMPTE
24 NTAPI
25 MiCheckVirtualAddress(IN PVOID VirtualAddress,
26 OUT PULONG ProtectCode,
27 OUT PMMVAD *ProtoVad)
28 {
29 PMMVAD Vad;
30 PMMPTE PointerPte;
31
32 /* No prototype/section support for now */
33 *ProtoVad = NULL;
34
35 /* Check if this is a page table address */
36 if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
37 {
38 /* This should never happen, as these addresses are handled by the double-maping */
39 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
40 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
41 {
42 /* Fail such access */
43 *ProtectCode = MM_NOACCESS;
44 return NULL;
45 }
46
47 /* Return full access rights */
48 *ProtectCode = MM_READWRITE;
49 return NULL;
50 }
51
52 /* Should not be a session address */
53 ASSERT(MI_IS_SESSION_ADDRESS(VirtualAddress) == FALSE);
54
55 /* Special case for shared data */
56 if (PAGE_ALIGN(VirtualAddress) == (PVOID)USER_SHARED_DATA)
57 {
58 /* It's a read-only page */
59 *ProtectCode = MM_READONLY;
60 return MmSharedUserDataPte;
61 }
62
63 /* Find the VAD, it might not exist if the address is bogus */
64 Vad = MiLocateAddress(VirtualAddress);
65 if (!Vad)
66 {
67 /* Bogus virtual address */
68 *ProtectCode = MM_NOACCESS;
69 return NULL;
70 }
71
72 /* This must be a VM VAD */
73 ASSERT(Vad->u.VadFlags.VadType == VadNone);
74
75 /* Check if it's a section, or just an allocation */
76 if (Vad->u.VadFlags.PrivateMemory == TRUE)
77 {
78 /* This must be a TEB/PEB VAD */
79 ASSERT(Vad->u.VadFlags.MemCommit == TRUE);
80 *ProtectCode = Vad->u.VadFlags.Protection;
81 return NULL;
82 }
83 else
84 {
85 /* Return the proto VAD */
86 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
87 *ProtoVad = Vad;
88
89 /* Get the prototype PTE for this page */
90 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
91 ASSERT(PointerPte <= Vad->LastContiguousPte);
92 ASSERT(PointerPte != NULL);
93
94 /* Return the Prototype PTE and the protection for the page mapping */
95 *ProtectCode = Vad->u.VadFlags.Protection;
96 return PointerPte;
97 }
98 }
99
100 NTSTATUS
101 FASTCALL
102 MiCheckPdeForPagedPool(IN PVOID Address)
103 {
104 PMMPDE PointerPde;
105 NTSTATUS Status = STATUS_SUCCESS;
106
107 /* No session support in ReactOS yet */
108 ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE);
109 ASSERT(MI_IS_SESSION_PTE(Address) == FALSE);
110
111 //
112 // Check if this is a fault while trying to access the page table itself
113 //
114 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
115 {
116 //
117 // Send a hint to the page fault handler that this is only a valid fault
118 // if we already detected this was access within the page table range
119 //
120 PointerPde = (PMMPDE)MiAddressToPte(Address);
121 Status = STATUS_WAIT_1;
122 }
123 else if (Address < MmSystemRangeStart)
124 {
125 //
126 // This is totally illegal
127 //
128 return STATUS_ACCESS_VIOLATION;
129 }
130 else
131 {
132 //
133 // Get the PDE for the address
134 //
135 PointerPde = MiAddressToPde(Address);
136 }
137
138 //
139 // Check if it's not valid
140 //
141 if (PointerPde->u.Hard.Valid == 0)
142 {
143 #ifdef _M_AMD64
144 ASSERT(FALSE);
145 #else
146 //
147 // Copy it from our double-mapped system page directory
148 //
149 InterlockedExchangePte(PointerPde,
150 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
151 #endif
152 }
153
154 //
155 // Return status
156 //
157 return Status;
158 }
159
160 VOID
161 NTAPI
162 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
163 {
164 PMMPTE ZeroPte;
165 MMPTE TempPte;
166 PMMPFN Pfn1;
167 PVOID ZeroAddress;
168
169 /* Get the PFN for this page */
170 Pfn1 = MiGetPfnEntry(PageFrameNumber);
171 ASSERT(Pfn1);
172
173 /* Grab a system PTE we can use to zero the page */
174 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
175 ASSERT(ZeroPte);
176
177 /* Initialize the PTE for it */
178 TempPte = ValidKernelPte;
179 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
180
181 /* Setup caching */
182 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
183 {
184 /* Write combining, no caching */
185 MI_PAGE_DISABLE_CACHE(&TempPte);
186 MI_PAGE_WRITE_COMBINED(&TempPte);
187 }
188 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
189 {
190 /* Write through, no caching */
191 MI_PAGE_DISABLE_CACHE(&TempPte);
192 MI_PAGE_WRITE_THROUGH(&TempPte);
193 }
194
195 /* Make the system PTE valid with our PFN */
196 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
197
198 /* Get the address it maps to, and zero it out */
199 ZeroAddress = MiPteToAddress(ZeroPte);
200 KeZeroPages(ZeroAddress, PAGE_SIZE);
201
202 /* Now get rid of it */
203 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
204 }
205
206 NTSTATUS
207 NTAPI
208 MiResolveDemandZeroFault(IN PVOID Address,
209 IN PMMPTE PointerPte,
210 IN PEPROCESS Process,
211 IN KIRQL OldIrql)
212 {
213 PFN_NUMBER PageFrameNumber = 0;
214 MMPTE TempPte;
215 BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
216 ULONG Color;
217 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
218 Address,
219 Process);
220
221 /* Must currently only be called by paging path */
222 if ((Process) && (OldIrql == MM_NOIRQL))
223 {
224 /* Sanity check */
225 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
226
227 /* No forking yet */
228 ASSERT(Process->ForkInProgress == NULL);
229
230 /* Get process color */
231 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
232 ASSERT(Color != 0xFFFFFFFF);
233
234 /* We'll need a zero page */
235 NeedZero = TRUE;
236 }
237 else
238 {
239 /* Check if we need a zero page */
240 NeedZero = (OldIrql != MM_NOIRQL);
241
242 /* Get the next system page color */
243 Color = MI_GET_NEXT_COLOR();
244 }
245
246 /* Check if the PFN database should be acquired */
247 if (OldIrql == MM_NOIRQL)
248 {
249 /* Acquire it and remember we should release it after */
250 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
251 HaveLock = TRUE;
252 }
253
254 /* We either manually locked the PFN DB, or already came with it locked */
255 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
256
257 /* Do we need a zero page? */
258 ASSERT(PointerPte->u.Hard.Valid == 0);
259 if ((NeedZero) && (Process))
260 {
261 /* Try to get one, if we couldn't grab a free page and zero it */
262 PageFrameNumber = MiRemoveZeroPageSafe(Color);
263 if (PageFrameNumber)
264 {
265 /* We got a genuine zero page, stop worrying about it */
266 NeedZero = FALSE;
267 }
268 else
269 {
270 /* We'll need a free page and zero it manually */
271 PageFrameNumber = MiRemoveAnyPage(Color);
272 }
273 }
274 else if (!NeedZero)
275 {
276 /* Process or system doesn't want a zero page, grab anything */
277 PageFrameNumber = MiRemoveAnyPage(Color);
278 }
279 else
280 {
281 /* System wants a zero page, obtain one */
282 PageFrameNumber = MiRemoveZeroPage(Color);
283 }
284
285 /* Initialize it */
286 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
287
288 /* Release PFN lock if needed */
289 if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
290
291 //
292 // Increment demand zero faults
293 //
294 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
295
296 /* Zero the page if need be */
297 if (NeedZero) MiZeroPfn(PageFrameNumber);
298
299 /* Build the PTE */
300 if (PointerPte <= MiHighestUserPte)
301 {
302 /* For user mode */
303 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
304 PointerPte,
305 PointerPte->u.Soft.Protection,
306 PageFrameNumber);
307 }
308 else
309 {
310 /* For kernel mode */
311 MI_MAKE_HARDWARE_PTE(&TempPte,
312 PointerPte,
313 PointerPte->u.Soft.Protection,
314 PageFrameNumber);
315 }
316
317 /* Set it dirty if it's a writable page */
318 if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
319
320 /* Write it */
321 MI_WRITE_VALID_PTE(PointerPte, TempPte);
322
323 //
324 // It's all good now
325 //
326 DPRINT("Paged pool page has now been paged in\n");
327 return STATUS_PAGE_FAULT_DEMAND_ZERO;
328 }
329
330 NTSTATUS
331 NTAPI
332 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
333 IN PVOID Address,
334 IN PMMPTE PointerPte,
335 IN PMMPTE PointerProtoPte,
336 IN KIRQL OldIrql,
337 IN PMMPFN Pfn1)
338 {
339 MMPTE TempPte;
340 PMMPTE OriginalPte;
341 ULONG Protection;
342 PFN_NUMBER PageFrameIndex;
343
344 /* Must be called with an valid prototype PTE, with the PFN lock held */
345 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
346 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
347
348 /* Get the page */
349 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
350
351 /* Get the PFN entry and set it as a prototype PTE */
352 Pfn1 = MiGetPfnEntry(PageFrameIndex);
353 Pfn1->u3.e1.PrototypePte = 1;
354
355 /* FIXME: Increment the share count for the page table */
356
357 /* Check where we should be getting the protection information from */
358 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
359 {
360 /* Get the protection from the PTE, there's no real Proto PTE data */
361 Protection = PointerPte->u.Soft.Protection;
362 }
363 else
364 {
365 /* Get the protection from the original PTE link */
366 OriginalPte = &Pfn1->OriginalPte;
367 Protection = OriginalPte->u.Soft.Protection;
368 }
369
370 /* Release the PFN lock */
371 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
372
373 /* Remove caching bits */
374 Protection &= ~(MM_NOCACHE | MM_NOACCESS);
375
376 /* Check if this is a kernel or user address */
377 if (Address < MmSystemRangeStart)
378 {
379 /* Build the user PTE */
380 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
381 }
382 else
383 {
384 /* Build the kernel PTE */
385 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
386 }
387
388 /* Write the PTE */
389 MI_WRITE_VALID_PTE(PointerPte, TempPte);
390
391 /* Return success */
392 return STATUS_SUCCESS;
393 }
394
395 NTSTATUS
396 NTAPI
397 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
398 IN PVOID Address,
399 IN PMMPTE PointerPte,
400 IN PMMPTE PointerProtoPte,
401 IN OUT PMMPFN *OutPfn,
402 OUT PVOID *PageFileData,
403 OUT PMMPTE PteValue,
404 IN PEPROCESS Process,
405 IN KIRQL OldIrql,
406 IN PVOID TrapInformation)
407 {
408 MMPTE TempPte;
409 PMMPFN Pfn1;
410 PFN_NUMBER PageFrameIndex;
411 NTSTATUS Status;
412
413 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
414 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
415 ASSERT(PointerPte->u.Hard.Valid == 0);
416 ASSERT(PointerPte->u.Soft.Prototype == 1);
417
418 /* Read the prototype PTE and check if it's valid */
419 TempPte = *PointerProtoPte;
420 if (TempPte.u.Hard.Valid == 1)
421 {
422 /* One more user of this mapped page */
423 PageFrameIndex = PFN_FROM_PTE(&TempPte);
424 Pfn1 = MiGetPfnEntry(PageFrameIndex);
425 Pfn1->u2.ShareCount++;
426
427 /* Call it a transition */
428 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
429
430 /* Complete the prototype PTE fault -- this will release the PFN lock */
431 return MiCompleteProtoPteFault(StoreInstruction,
432 Address,
433 PointerPte,
434 PointerProtoPte,
435 OldIrql,
436 NULL);
437 }
438
439 /* Make sure there's some protection mask */
440 if (TempPte.u.Long == 0)
441 {
442 /* Release the lock */
443 DPRINT1("Access on reserved section?\n");
444 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
445 return STATUS_ACCESS_VIOLATION;
446 }
447
448 /* This is the only thing we support right now */
449 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
450 ASSERT(TempPte.u.Proto.ReadOnly == 0);
451 ASSERT(PointerPte > MiHighestUserPte);
452 ASSERT(TempPte.u.Soft.Prototype == 0);
453 ASSERT(TempPte.u.Soft.Transition == 0);
454
455 /* Resolve the demand zero fault */
456 Status = MiResolveDemandZeroFault(Address, PointerProtoPte, Process, OldIrql);
457 ASSERT(NT_SUCCESS(Status));
458
459 /* Complete the prototype PTE fault -- this will release the PFN lock */
460 ASSERT(PointerPte->u.Hard.Valid == 0);
461 return MiCompleteProtoPteFault(StoreInstruction,
462 Address,
463 PointerPte,
464 PointerProtoPte,
465 OldIrql,
466 NULL);
467 }
468
469 NTSTATUS
470 NTAPI
471 MiDispatchFault(IN BOOLEAN StoreInstruction,
472 IN PVOID Address,
473 IN PMMPTE PointerPte,
474 IN PMMPTE PointerProtoPte,
475 IN BOOLEAN Recursive,
476 IN PEPROCESS Process,
477 IN PVOID TrapInformation,
478 IN PVOID Vad)
479 {
480 MMPTE TempPte;
481 KIRQL OldIrql, LockIrql;
482 NTSTATUS Status;
483 PMMPTE SuperProtoPte;
484 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
485 Address,
486 Process);
487
488 //
489 // Make sure APCs are off and we're not at dispatch
490 //
491 OldIrql = KeGetCurrentIrql();
492 ASSERT(OldIrql <= APC_LEVEL);
493 ASSERT(KeAreAllApcsDisabled() == TRUE);
494
495 //
496 // Grab a copy of the PTE
497 //
498 TempPte = *PointerPte;
499
500 /* Do we have a prototype PTE? */
501 if (PointerProtoPte)
502 {
503 /* This should never happen */
504 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
505
506 /* Check if this is a kernel-mode address */
507 SuperProtoPte = MiAddressToPte(PointerProtoPte);
508 if (Address >= MmSystemRangeStart)
509 {
510 /* Lock the PFN database */
511 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
512
513 /* Has the PTE been made valid yet? */
514 if (!SuperProtoPte->u.Hard.Valid)
515 {
516 UNIMPLEMENTED;
517 while (TRUE);
518 }
519 else
520 {
521 /* Resolve the fault -- this will release the PFN lock */
522 ASSERT(PointerPte->u.Hard.Valid == 0);
523 Status = MiResolveProtoPteFault(StoreInstruction,
524 Address,
525 PointerPte,
526 PointerProtoPte,
527 NULL,
528 NULL,
529 NULL,
530 Process,
531 LockIrql,
532 TrapInformation);
533 ASSERT(Status == STATUS_SUCCESS);
534
535 /* Complete this as a transition fault */
536 ASSERT(OldIrql == KeGetCurrentIrql());
537 ASSERT(OldIrql <= APC_LEVEL);
538 ASSERT(KeAreAllApcsDisabled() == TRUE);
539 return Status;
540 }
541 }
542 else
543 {
544 /* We currently only handle very limited paths */
545 ASSERT(PointerPte->u.Soft.Prototype == 1);
546 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
547
548 /* Lock the PFN database */
549 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
550
551 /* For our current usage, this should be true */
552 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
553 ASSERT(TempPte.u.Hard.Valid == 0);
554
555 /* Resolve the fault -- this will release the PFN lock */
556 Status = MiResolveProtoPteFault(StoreInstruction,
557 Address,
558 PointerPte,
559 PointerProtoPte,
560 NULL,
561 NULL,
562 NULL,
563 Process,
564 LockIrql,
565 TrapInformation);
566 ASSERT(Status == STATUS_SUCCESS);
567
568 /* Complete this as a transition fault */
569 ASSERT(OldIrql == KeGetCurrentIrql());
570 ASSERT(OldIrql <= APC_LEVEL);
571 ASSERT(KeAreAllApcsDisabled() == TRUE);
572 return STATUS_PAGE_FAULT_TRANSITION;
573 }
574 }
575
576 //
577 // The PTE must be invalid, but not totally blank
578 //
579 ASSERT(TempPte.u.Hard.Valid == 0);
580 ASSERT(TempPte.u.Long != 0);
581
582 //
583 // No prototype, transition or page file software PTEs in ARM3 yet
584 //
585 ASSERT(TempPte.u.Soft.Prototype == 0);
586 ASSERT(TempPte.u.Soft.Transition == 0);
587 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
588
589 //
590 // If we got this far, the PTE can only be a demand zero PTE, which is what
591 // we want. Go handle it!
592 //
593 Status = MiResolveDemandZeroFault(Address,
594 PointerPte,
595 Process,
596 MM_NOIRQL);
597 ASSERT(KeAreAllApcsDisabled () == TRUE);
598 if (NT_SUCCESS(Status))
599 {
600 //
601 // Make sure we're returning in a sane state and pass the status down
602 //
603 ASSERT(OldIrql == KeGetCurrentIrql ());
604 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
605 return Status;
606 }
607
608 //
609 // Generate an access fault
610 //
611 return STATUS_ACCESS_VIOLATION;
612 }
613
614 NTSTATUS
615 NTAPI
616 MmArmAccessFault(IN BOOLEAN StoreInstruction,
617 IN PVOID Address,
618 IN KPROCESSOR_MODE Mode,
619 IN PVOID TrapInformation)
620 {
621 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
622 PMMPTE PointerPte, ProtoPte = NULL;
623 PMMPDE PointerPde;
624 MMPTE TempPte;
625 PETHREAD CurrentThread;
626 PEPROCESS CurrentProcess;
627 NTSTATUS Status;
628 PMMSUPPORT WorkingSet;
629 ULONG ProtectionCode;
630 PMMVAD Vad;
631 PFN_NUMBER PageFrameIndex;
632 ULONG Color;
633 DPRINT("ARM3 FAULT AT: %p\n", Address);
634
635 //
636 // Get the PTE and PDE
637 //
638 PointerPte = MiAddressToPte(Address);
639 PointerPde = MiAddressToPde(Address);
640 #if (_MI_PAGING_LEVELS >= 3)
641 /* We need the PPE and PXE addresses */
642 ASSERT(FALSE);
643 #endif
644
645 //
646 // Check for dispatch-level snafu
647 //
648 if (OldIrql > APC_LEVEL)
649 {
650 //
651 // There are some special cases where this is okay, but not in ARM3 yet
652 //
653 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
654 Address,
655 OldIrql);
656 ASSERT(OldIrql <= APC_LEVEL);
657 }
658
659 //
660 // Check for kernel fault
661 //
662 while (Address >= MmSystemRangeStart)
663 {
664 //
665 // What are you even DOING here?
666 //
667 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
668
669 #if (_MI_PAGING_LEVELS >= 3)
670 /* Need to check PXE and PDE validity */
671 ASSERT(FALSE);
672 #endif
673
674 //
675 // Is the PDE valid?
676 //
677 if (!PointerPde->u.Hard.Valid == 0)
678 {
679 //
680 // Debug spew (eww!)
681 //
682 DPRINT("Invalid PDE\n");
683 #if (_MI_PAGING_LEVELS == 2)
684 //
685 // Handle mapping in "Special" PDE directoreis
686 //
687 MiCheckPdeForPagedPool(Address);
688 #endif
689 //
690 // Now we SHOULD be good
691 //
692 if (PointerPde->u.Hard.Valid == 0)
693 {
694 //
695 // FIXFIX: Do the S-LIST hack
696 //
697
698 //
699 // Kill the system
700 //
701 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
702 (ULONG_PTR)Address,
703 StoreInstruction,
704 (ULONG_PTR)TrapInformation,
705 2);
706 }
707 }
708
709 //
710 // The PDE is valid, so read the PTE
711 //
712 TempPte = *PointerPte;
713 if (TempPte.u.Hard.Valid == 1)
714 {
715 //
716 // Only two things can go wrong here:
717 // Executing NX page (we couldn't care less)
718 // Writing to a read-only page (the stuff ARM3 works with is write,
719 // so again, moot point).
720 //
721 if (StoreInstruction)
722 {
723 DPRINT1("Should NEVER happen on ARM3!!!\n");
724 return STATUS_ACCESS_VIOLATION;
725 }
726
727 //
728 // Otherwise, the PDE was probably invalid, and all is good now
729 //
730 return STATUS_SUCCESS;
731 }
732
733 //
734 // Check for a fault on the page table or hyperspace itself
735 //
736 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
737 {
738 #if (_MI_PAGING_LEVELS == 2)
739 /* Could be paged pool access from a new process -- synchronize the page directories */
740 if (MiCheckPdeForPagedPool(Address) == STATUS_WAIT_1)
741 {
742 DPRINT1("PAGE TABLES FAULTED IN!\n");
743 return STATUS_SUCCESS;
744 }
745 #endif
746 /* Otherwise this could be a commit of a virtual address */
747 break;
748 }
749
750 /* In this path, we are using the system working set */
751 CurrentThread = PsGetCurrentThread();
752 WorkingSet = &MmSystemCacheWs;
753
754 /* Acquire it */
755 KeRaiseIrql(APC_LEVEL, &LockIrql);
756 MiLockWorkingSet(CurrentThread, WorkingSet);
757
758 //
759 // Re-read PTE now that the IRQL has been raised
760 //
761 TempPte = *PointerPte;
762 if (TempPte.u.Hard.Valid == 1)
763 {
764 //
765 // Only two things can go wrong here:
766 // Executing NX page (we couldn't care less)
767 // Writing to a read-only page (the stuff ARM3 works with is write,
768 // so again, moot point.
769 //
770 if (StoreInstruction)
771 {
772 DPRINT1("Should NEVER happen on ARM3!!!\n");
773 return STATUS_ACCESS_VIOLATION;
774 }
775
776 /* Release the working set */
777 MiUnlockWorkingSet(CurrentThread, WorkingSet);
778 KeLowerIrql(LockIrql);
779
780 //
781 // Otherwise, the PDE was probably invalid, and all is good now
782 //
783 return STATUS_SUCCESS;
784 }
785
786 /* Check one kind of prototype PTE */
787 if (TempPte.u.Soft.Prototype)
788 {
789 /* Make sure protected pool is on, and that this is a pool address */
790 if ((MmProtectFreedNonPagedPool) &&
791 (((Address >= MmNonPagedPoolStart) &&
792 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
793 MmSizeOfNonPagedPoolInBytes))) ||
794 ((Address >= MmNonPagedPoolExpansionStart) &&
795 (Address < MmNonPagedPoolEnd))))
796 {
797 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
798 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
799 (ULONG_PTR)Address,
800 StoreInstruction,
801 Mode,
802 4);
803 }
804
805 /* Get the prototype PTE! */
806 ProtoPte = MiProtoPteToPte(&TempPte);
807 }
808 else
809 {
810 //
811 // We don't implement transition PTEs
812 //
813 ASSERT(TempPte.u.Soft.Transition == 0);
814
815 /* Check for no-access PTE */
816 if (TempPte.u.Soft.Protection == MM_NOACCESS)
817 {
818 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
819 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
820 (ULONG_PTR)Address,
821 StoreInstruction,
822 (ULONG_PTR)TrapInformation,
823 1);
824 }
825 }
826
827 /* Check for demand page */
828 if ((StoreInstruction) && !(ProtoPte) && !(TempPte.u.Hard.Valid))
829 {
830 /* Get the protection code */
831 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
832 {
833 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
834 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
835 (ULONG_PTR)Address,
836 TempPte.u.Long,
837 (ULONG_PTR)TrapInformation,
838 14);
839 }
840 }
841
842 //
843 // Now do the real fault handling
844 //
845 Status = MiDispatchFault(StoreInstruction,
846 Address,
847 PointerPte,
848 ProtoPte,
849 FALSE,
850 NULL,
851 TrapInformation,
852 NULL);
853
854 /* Release the working set */
855 ASSERT(KeAreAllApcsDisabled() == TRUE);
856 MiUnlockWorkingSet(CurrentThread, WorkingSet);
857 KeLowerIrql(LockIrql);
858
859 //
860 // We are done!
861 //
862 DPRINT("Fault resolved with status: %lx\n", Status);
863 return Status;
864 }
865
866 /* This is a user fault */
867 CurrentThread = PsGetCurrentThread();
868 CurrentProcess = PsGetCurrentProcess();
869
870 /* Lock the working set */
871 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
872
873 #if (_MI_PAGING_LEVELS >= 3)
874 /* Need to check/handle PPE and PXE validity too */
875 ASSERT(FALSE);
876 #endif
877
878 /* First things first, is the PDE valid? */
879 ASSERT(PointerPde->u.Hard.LargePage == 0);
880 if (PointerPde->u.Hard.Valid == 0)
881 {
882 /* Right now, we only handle scenarios where the PDE is totally empty */
883 ASSERT(PointerPde->u.Long == 0);
884
885 /* Check if this address range belongs to a valid allocation (VAD) */
886 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
887
888 /* Right now, we expect a valid protection mask on the VAD */
889 ASSERT(ProtectionCode != MM_NOACCESS);
890
891 /* Make the PDE demand-zero */
892 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
893
894 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
895 Status = MiDispatchFault(TRUE,
896 PointerPte,
897 PointerPde,
898 NULL,
899 FALSE,
900 PsGetCurrentProcess(),
901 TrapInformation,
902 NULL);
903
904 /* We should come back with APCs enabled, and with a valid PDE */
905 ASSERT(KeAreAllApcsDisabled() == TRUE);
906 #if (_MI_PAGING_LEVELS >= 3)
907 /* Need to check/handle PPE and PXE validity too */
908 ASSERT(FALSE);
909 #endif
910 ASSERT(PointerPde->u.Hard.Valid == 1);
911 }
912
913 /* Now capture the PTE. Ignore virtual faults for now */
914 TempPte = *PointerPte;
915 ASSERT(TempPte.u.Hard.Valid == 0);
916
917 /* Quick check for demand-zero */
918 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
919 {
920 /* Resolve the fault */
921 MiResolveDemandZeroFault(Address,
922 PointerPte,
923 CurrentProcess,
924 MM_NOIRQL);
925
926 /* Return the status */
927 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
928 return STATUS_PAGE_FAULT_DEMAND_ZERO;
929 }
930
931 /* Get protection and check if it's a prototype PTE */
932 ProtectionCode = TempPte.u.Soft.Protection;
933 ASSERT(TempPte.u.Soft.Prototype == 0);
934
935 /* Check for non-demand zero PTE */
936 if (TempPte.u.Long != 0)
937 {
938 /* This is a page fault, check for valid protection */
939 ASSERT(ProtectionCode != 0x100);
940
941 /* FIXME: Run MiAccessCheck */
942
943 /* Dispatch the fault */
944 Status = MiDispatchFault(StoreInstruction,
945 Address,
946 PointerPte,
947 NULL,
948 FALSE,
949 PsGetCurrentProcess(),
950 TrapInformation,
951 NULL);
952
953 /* Return the status */
954 ASSERT(NT_SUCCESS(Status));
955 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
956 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
957 return Status;
958 }
959
960 /* Check if this address range belongs to a valid allocation (VAD) */
961 ASSERT(TempPte.u.Long == 0);
962 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
963 if (ProtectionCode == MM_NOACCESS)
964 {
965 /* This is a bogus VA */
966 Status = STATUS_ACCESS_VIOLATION;
967
968 /* Could be a not-yet-mapped paged pool page table */
969 #if (_MI_PAGING_LEVELS == 2)
970 MiCheckPdeForPagedPool(Address);
971 #endif
972 /* See if that fixed it */
973 if (PointerPte->u.Hard.Valid == 1) Status = STATUS_SUCCESS;
974
975 /* Return the status */
976 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
977 return Status;
978 }
979
980 /* Did we get a prototype PTE back? */
981 if (!ProtoPte)
982 {
983 /* No, create a new PTE. First, write the protection */
984 PointerPte->u.Soft.Protection = ProtectionCode;
985
986 /* Lock the PFN database since we're going to grab a page */
987 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
988
989 /* Try to get a zero page */
990 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
991 PageFrameIndex = MiRemoveZeroPageSafe(Color);
992 if (!PageFrameIndex)
993 {
994 /* Grab a page out of there. Later we should grab a colored zero page */
995 PageFrameIndex = MiRemoveAnyPage(Color);
996 ASSERT(PageFrameIndex);
997
998 /* Release the lock since we need to do some zeroing */
999 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1000
1001 /* Zero out the page, since it's for user-mode */
1002 MiZeroPfn(PageFrameIndex);
1003
1004 /* Grab the lock again so we can initialize the PFN entry */
1005 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1006 }
1007
1008 /* Initialize the PFN entry now */
1009 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1010
1011 /* And we're done with the lock */
1012 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1013
1014 /* One more demand-zero fault */
1015 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1016
1017 /* Was the fault on an actual user page, or a kernel page for the user? */
1018 if (PointerPte <= MiHighestUserPte)
1019 {
1020 /* User fault, build a user PTE */
1021 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1022 PointerPte,
1023 PointerPte->u.Soft.Protection,
1024 PageFrameIndex);
1025 }
1026 else
1027 {
1028 /* Session, kernel, or user PTE, figure it out and build it */
1029 MI_MAKE_HARDWARE_PTE(&TempPte,
1030 PointerPte,
1031 PointerPte->u.Soft.Protection,
1032 PageFrameIndex);
1033 }
1034
1035 /* Write the dirty bit for writeable pages */
1036 if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
1037
1038 /* And now write down the PTE, making the address valid */
1039 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1040
1041 /* Demand zero */
1042 Status = STATUS_PAGE_FAULT_DEMAND_ZERO;
1043 }
1044 else
1045 {
1046 /* No guard page support yet */
1047 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1048 ASSERT(ProtectionCode != 0x100);
1049
1050 /* Write the prototype PTE */
1051 TempPte = PrototypePte;
1052 TempPte.u.Soft.Protection = ProtectionCode;
1053 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1054
1055 /* Handle the fault */
1056 Status = MiDispatchFault(StoreInstruction,
1057 Address,
1058 PointerPte,
1059 ProtoPte,
1060 FALSE,
1061 CurrentProcess,
1062 TrapInformation,
1063 Vad);
1064 ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION);
1065 ASSERT(PointerPte->u.Hard.Valid == 1);
1066 ASSERT(PointerPte->u.Hard.PageFrameNumber != 0);
1067 }
1068
1069 /* Release the working set */
1070 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1071 return Status;
1072 }
1073
1074 /* EOF */