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