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