Sync with trunk head (r48654)
[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
31 /* No prototype/section support for now */
32 *ProtoVad = NULL;
33
34 /* Only valid for user VADs for now */
35 ASSERT(VirtualAddress <= MM_HIGHEST_USER_ADDRESS);
36
37 /* Special case for shared data */
38 if (PAGE_ALIGN(VirtualAddress) == (PVOID)USER_SHARED_DATA)
39 {
40 /* It's a read-only page */
41 *ProtectCode = MM_READONLY;
42 return MmSharedUserDataPte;
43 }
44
45 /* Find the VAD, it must exist, since we only handle PEB/TEB */
46 Vad = MiLocateAddress(VirtualAddress);
47 ASSERT(Vad);
48
49 /* This must be a TEB/PEB VAD */
50 ASSERT(Vad->u.VadFlags.PrivateMemory == TRUE);
51 ASSERT(Vad->u.VadFlags.MemCommit == TRUE);
52 ASSERT(Vad->u.VadFlags.VadType == VadNone);
53
54 /* Return the protection on it */
55 *ProtectCode = Vad->u.VadFlags.Protection;
56 return NULL;
57 }
58
59 NTSTATUS
60 FASTCALL
61 MiCheckPdeForPagedPool(IN PVOID Address)
62 {
63 PMMPDE PointerPde;
64 NTSTATUS Status = STATUS_SUCCESS;
65
66 /* No session support in ReactOS yet */
67 ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE);
68 ASSERT(MI_IS_SESSION_PTE(Address) == FALSE);
69
70 //
71 // Check if this is a fault while trying to access the page table itself
72 //
73 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
74 {
75 //
76 // Send a hint to the page fault handler that this is only a valid fault
77 // if we already detected this was access within the page table range
78 //
79 PointerPde = (PMMPDE)MiAddressToPte(Address);
80 Status = STATUS_WAIT_1;
81 }
82 else if (Address < MmSystemRangeStart)
83 {
84 //
85 // This is totally illegal
86 //
87 return STATUS_ACCESS_VIOLATION;
88 }
89 else
90 {
91 //
92 // Get the PDE for the address
93 //
94 PointerPde = MiAddressToPde(Address);
95 }
96
97 //
98 // Check if it's not valid
99 //
100 if (PointerPde->u.Hard.Valid == 0)
101 {
102 #ifdef _M_AMD64
103 ASSERT(FALSE);
104 #else
105 /* This seems to be making the assumption that one PDE is one page long */
106 C_ASSERT(PAGE_SIZE == (PD_COUNT * (sizeof(MMPTE) * PDE_COUNT)));
107
108 //
109 // Copy it from our double-mapped system page directory
110 //
111 InterlockedExchangePte(PointerPde,
112 MmSystemPagePtes[((ULONG_PTR)PointerPde &
113 (PAGE_SIZE - 1)) /
114 sizeof(MMPTE)].u.Long);
115 #endif
116 }
117
118 //
119 // Return status
120 //
121 return Status;
122 }
123
124 VOID
125 NTAPI
126 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
127 {
128 PMMPTE ZeroPte;
129 MMPTE TempPte;
130 PMMPFN Pfn1;
131 PVOID ZeroAddress;
132
133 /* Get the PFN for this page */
134 Pfn1 = MiGetPfnEntry(PageFrameNumber);
135 ASSERT(Pfn1);
136
137 /* Grab a system PTE we can use to zero the page */
138 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
139 ASSERT(ZeroPte);
140
141 /* Initialize the PTE for it */
142 TempPte = ValidKernelPte;
143 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
144
145 /* Setup caching */
146 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
147 {
148 /* Write combining, no caching */
149 MI_PAGE_DISABLE_CACHE(&TempPte);
150 MI_PAGE_WRITE_COMBINED(&TempPte);
151 }
152 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
153 {
154 /* Write through, no caching */
155 MI_PAGE_DISABLE_CACHE(&TempPte);
156 MI_PAGE_WRITE_THROUGH(&TempPte);
157 }
158
159 /* Make the system PTE valid with our PFN */
160 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
161
162 /* Get the address it maps to, and zero it out */
163 ZeroAddress = MiPteToAddress(ZeroPte);
164 KeZeroPages(ZeroAddress, PAGE_SIZE);
165
166 /* Now get rid of it */
167 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
168 }
169
170 NTSTATUS
171 NTAPI
172 MiResolveDemandZeroFault(IN PVOID Address,
173 IN PMMPTE PointerPte,
174 IN PEPROCESS Process,
175 IN KIRQL OldIrql)
176 {
177 PFN_NUMBER PageFrameNumber;
178 MMPTE TempPte;
179 BOOLEAN NeedZero = FALSE;
180 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
181 Address,
182 Process);
183
184 /* Must currently only be called by paging path */
185 ASSERT(OldIrql == MM_NOIRQL);
186 if (Process)
187 {
188 /* Sanity check */
189 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
190
191 /* No forking yet */
192 ASSERT(Process->ForkInProgress == NULL);
193
194 /* We'll need a zero page */
195 NeedZero = TRUE;
196 }
197
198 //
199 // Lock the PFN database
200 //
201 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
202 ASSERT(PointerPte->u.Hard.Valid == 0);
203
204 /* Get a page */
205 PageFrameNumber = MiRemoveAnyPage(0);
206 DPRINT("New pool page: %lx\n", PageFrameNumber);
207
208 /* Initialize it */
209 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
210
211 //
212 // Release PFN lock
213 //
214 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
215
216 //
217 // Increment demand zero faults
218 //
219 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
220
221 /* Zero the page if need be */
222 if (NeedZero) MiZeroPfn(PageFrameNumber);
223
224 /* Build the PTE */
225 if (PointerPte <= MiHighestUserPte)
226 {
227 /* For user mode */
228 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
229 PointerPte,
230 PointerPte->u.Soft.Protection,
231 PageFrameNumber);
232 }
233 else
234 {
235 /* For kernel mode */
236 MI_MAKE_HARDWARE_PTE(&TempPte,
237 PointerPte,
238 PointerPte->u.Soft.Protection,
239 PageFrameNumber);
240 }
241
242 /* Set it dirty if it's a writable page */
243 if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
244
245 /* Write it */
246 MI_WRITE_VALID_PTE(PointerPte, TempPte);
247
248 //
249 // It's all good now
250 //
251 DPRINT("Paged pool page has now been paged in\n");
252 return STATUS_PAGE_FAULT_DEMAND_ZERO;
253 }
254
255 NTSTATUS
256 NTAPI
257 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
258 IN PVOID Address,
259 IN PMMPTE PointerPte,
260 IN PMMPTE PointerProtoPte,
261 IN KIRQL OldIrql,
262 IN PMMPFN Pfn1)
263 {
264 MMPTE TempPte;
265 PFN_NUMBER PageFrameIndex;
266
267 /* Must be called with an valid prototype PTE, with the PFN lock held */
268 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
269 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
270
271 /* Quick-n-dirty */
272 ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF);
273
274 /* Get the page */
275 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
276
277 /* Release the PFN lock */
278 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
279
280 /* Build the user PTE */
281 ASSERT(Address < MmSystemRangeStart);
282 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, MM_READONLY, PageFrameIndex);
283
284 /* Write the PTE */
285 MI_WRITE_VALID_PTE(PointerPte, TempPte);
286
287 /* Return success */
288 return STATUS_SUCCESS;
289 }
290
291 NTSTATUS
292 NTAPI
293 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
294 IN PVOID Address,
295 IN PMMPTE PointerPte,
296 IN PMMPTE PointerProtoPte,
297 IN OUT PMMPFN *OutPfn,
298 OUT PVOID *PageFileData,
299 OUT PMMPTE PteValue,
300 IN PEPROCESS Process,
301 IN KIRQL OldIrql,
302 IN PVOID TrapInformation)
303 {
304 MMPTE TempPte;
305 PMMPFN Pfn1;
306 PFN_NUMBER PageFrameIndex;
307
308 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
309 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
310 ASSERT(PointerPte->u.Hard.Valid == 0);
311 ASSERT(PointerPte->u.Soft.Prototype == 1);
312
313 /* Read the prototype PTE -- it must be valid since we only handle shared data */
314 TempPte = *PointerProtoPte;
315 ASSERT(TempPte.u.Hard.Valid == 1);
316
317 /* One more user of this mapped page */
318 PageFrameIndex = PFN_FROM_PTE(&TempPte);
319 Pfn1 = MiGetPfnEntry(PageFrameIndex);
320 Pfn1->u2.ShareCount++;
321
322 /* Call it a transition */
323 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
324
325 /* Complete the prototype PTE fault -- this will release the PFN lock */
326 return MiCompleteProtoPteFault(StoreInstruction,
327 Address,
328 PointerPte,
329 PointerProtoPte,
330 OldIrql,
331 NULL);
332 }
333
334 NTSTATUS
335 NTAPI
336 MiDispatchFault(IN BOOLEAN StoreInstruction,
337 IN PVOID Address,
338 IN PMMPTE PointerPte,
339 IN PMMPTE PointerProtoPte,
340 IN BOOLEAN Recursive,
341 IN PEPROCESS Process,
342 IN PVOID TrapInformation,
343 IN PVOID Vad)
344 {
345 MMPTE TempPte;
346 KIRQL OldIrql, LockIrql;
347 NTSTATUS Status;
348 PMMPTE SuperProtoPte;
349 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
350 Address,
351 Process);
352
353 //
354 // Make sure APCs are off and we're not at dispatch
355 //
356 OldIrql = KeGetCurrentIrql();
357 ASSERT(OldIrql <= APC_LEVEL);
358 ASSERT(KeAreAllApcsDisabled() == TRUE);
359
360 //
361 // Grab a copy of the PTE
362 //
363 TempPte = *PointerPte;
364
365 /* Do we have a prototype PTE? */
366 if (PointerProtoPte)
367 {
368 /* This should never happen */
369 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
370
371 /* We currently only handle the shared user data PTE path */
372 ASSERT(Address < MmSystemRangeStart);
373 ASSERT(PointerPte->u.Soft.Prototype == 1);
374 ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF);
375 ASSERT(Vad == NULL);
376
377 /* Lock the PFN database */
378 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
379
380 /* For the shared data page, this should be true */
381 SuperProtoPte = MiAddressToPte(PointerProtoPte);
382 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
383 ASSERT(TempPte.u.Hard.Valid == 0);
384
385 /* Resolve the fault -- this will release the PFN lock */
386 Status = MiResolveProtoPteFault(StoreInstruction,
387 Address,
388 PointerPte,
389 PointerProtoPte,
390 NULL,
391 NULL,
392 NULL,
393 Process,
394 LockIrql,
395 TrapInformation);
396 ASSERT(Status == STATUS_SUCCESS);
397
398 /* Complete this as a transition fault */
399 ASSERT(OldIrql == KeGetCurrentIrql());
400 ASSERT(OldIrql <= APC_LEVEL);
401 ASSERT(KeAreAllApcsDisabled() == TRUE);
402 return STATUS_PAGE_FAULT_TRANSITION;
403 }
404
405 //
406 // The PTE must be invalid, but not totally blank
407 //
408 ASSERT(TempPte.u.Hard.Valid == 0);
409 ASSERT(TempPte.u.Long != 0);
410
411 //
412 // No prototype, transition or page file software PTEs in ARM3 yet
413 //
414 ASSERT(TempPte.u.Soft.Prototype == 0);
415 ASSERT(TempPte.u.Soft.Transition == 0);
416 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
417
418 //
419 // If we got this far, the PTE can only be a demand zero PTE, which is what
420 // we want. Go handle it!
421 //
422 Status = MiResolveDemandZeroFault(Address,
423 PointerPte,
424 Process,
425 MM_NOIRQL);
426 ASSERT(KeAreAllApcsDisabled () == TRUE);
427 if (NT_SUCCESS(Status))
428 {
429 //
430 // Make sure we're returning in a sane state and pass the status down
431 //
432 ASSERT(OldIrql == KeGetCurrentIrql ());
433 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
434 return Status;
435 }
436
437 //
438 // Generate an access fault
439 //
440 return STATUS_ACCESS_VIOLATION;
441 }
442
443 NTSTATUS
444 NTAPI
445 MmArmAccessFault(IN BOOLEAN StoreInstruction,
446 IN PVOID Address,
447 IN KPROCESSOR_MODE Mode,
448 IN PVOID TrapInformation)
449 {
450 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
451 PMMPTE PointerPte, ProtoPte;
452 PMMPDE PointerPde;
453 MMPTE TempPte;
454 PETHREAD CurrentThread;
455 PEPROCESS CurrentProcess;
456 NTSTATUS Status;
457 PMMSUPPORT WorkingSet;
458 ULONG ProtectionCode;
459 PMMVAD Vad;
460 PFN_NUMBER PageFrameIndex;
461 DPRINT("ARM3 FAULT AT: %p\n", Address);
462
463 //
464 // Get the PTE and PDE
465 //
466 PointerPte = MiAddressToPte(Address);
467 PointerPde = MiAddressToPde(Address);
468 #if (_MI_PAGING_LEVELS >= 3)
469 /* We need the PPE and PXE addresses */
470 ASSERT(FALSE);
471 #endif
472
473 //
474 // Check for dispatch-level snafu
475 //
476 if (OldIrql > APC_LEVEL)
477 {
478 //
479 // There are some special cases where this is okay, but not in ARM3 yet
480 //
481 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
482 Address,
483 OldIrql);
484 ASSERT(OldIrql <= APC_LEVEL);
485 }
486
487 //
488 // Check for kernel fault
489 //
490 if (Address >= MmSystemRangeStart)
491 {
492 //
493 // What are you even DOING here?
494 //
495 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
496
497 #if (_MI_PAGING_LEVELS >= 3)
498 /* Need to check PXE and PDE validity */
499 ASSERT(FALSE);
500 #endif
501
502 //
503 // Is the PDE valid?
504 //
505 if (!PointerPde->u.Hard.Valid == 0)
506 {
507 //
508 // Debug spew (eww!)
509 //
510 DPRINT("Invalid PDE\n");
511 #if (_MI_PAGING_LEVELS == 2)
512 //
513 // Handle mapping in "Special" PDE directoreis
514 //
515 MiCheckPdeForPagedPool(Address);
516 #endif
517 //
518 // Now we SHOULD be good
519 //
520 if (PointerPde->u.Hard.Valid == 0)
521 {
522 //
523 // FIXFIX: Do the S-LIST hack
524 //
525
526 //
527 // Kill the system
528 //
529 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
530 (ULONG_PTR)Address,
531 StoreInstruction,
532 (ULONG_PTR)TrapInformation,
533 2);
534 }
535 }
536
537 //
538 // The PDE is valid, so read the PTE
539 //
540 TempPte = *PointerPte;
541 if (TempPte.u.Hard.Valid == 1)
542 {
543 //
544 // Only two things can go wrong here:
545 // Executing NX page (we couldn't care less)
546 // Writing to a read-only page (the stuff ARM3 works with is write,
547 // so again, moot point).
548 //
549 if (StoreInstruction)
550 {
551 DPRINT1("Should NEVER happen on ARM3!!!\n");
552 return STATUS_ACCESS_VIOLATION;
553 }
554
555 //
556 // Otherwise, the PDE was probably invalid, and all is good now
557 //
558 return STATUS_SUCCESS;
559 }
560
561 //
562 // Check for a fault on the page table or hyperspace itself
563 //
564 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
565 {
566 //
567 // This might happen...not sure yet
568 //
569 DPRINT1("FAULT ON PAGE TABLES: %p %lx %lx!\n", Address, *PointerPte, *PointerPde);
570 #if (_MI_PAGING_LEVELS == 2)
571 //
572 // Map in the page table
573 //
574 if (MiCheckPdeForPagedPool(Address) == STATUS_WAIT_1)
575 {
576 DPRINT1("PAGE TABLES FAULTED IN!\n");
577 return STATUS_SUCCESS;
578 }
579 #endif
580 //
581 // Otherwise the page table doesn't actually exist
582 //
583 DPRINT1("FAILING\n");
584 return STATUS_ACCESS_VIOLATION;
585 }
586
587 /* In this path, we are using the system working set */
588 CurrentThread = PsGetCurrentThread();
589 WorkingSet = &MmSystemCacheWs;
590
591 /* Acquire it */
592 KeRaiseIrql(APC_LEVEL, &LockIrql);
593 MiLockWorkingSet(CurrentThread, WorkingSet);
594
595 //
596 // Re-read PTE now that the IRQL has been raised
597 //
598 TempPte = *PointerPte;
599 if (TempPte.u.Hard.Valid == 1)
600 {
601 //
602 // Only two things can go wrong here:
603 // Executing NX page (we couldn't care less)
604 // Writing to a read-only page (the stuff ARM3 works with is write,
605 // so again, moot point.
606 //
607 if (StoreInstruction)
608 {
609 DPRINT1("Should NEVER happen on ARM3!!!\n");
610 return STATUS_ACCESS_VIOLATION;
611 }
612
613 /* Release the working set */
614 MiUnlockWorkingSet(CurrentThread, WorkingSet);
615 KeLowerIrql(LockIrql);
616
617 //
618 // Otherwise, the PDE was probably invalid, and all is good now
619 //
620 return STATUS_SUCCESS;
621 }
622
623 /* Check one kind of prototype PTE */
624 if (TempPte.u.Soft.Prototype)
625 {
626 /* The one used for protected pool... */
627 ASSERT(MmProtectFreedNonPagedPool == TRUE);
628
629 /* Make sure protected pool is on, and that this is a pool address */
630 if ((MmProtectFreedNonPagedPool) &&
631 (((Address >= MmNonPagedPoolStart) &&
632 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
633 MmSizeOfNonPagedPoolInBytes))) ||
634 ((Address >= MmNonPagedPoolExpansionStart) &&
635 (Address < MmNonPagedPoolEnd))))
636 {
637 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
638 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
639 (ULONG_PTR)Address,
640 StoreInstruction,
641 Mode,
642 4);
643 }
644 }
645
646 //
647 // We don't implement transition PTEs
648 //
649 ASSERT(TempPte.u.Soft.Transition == 0);
650
651 //
652 // Now do the real fault handling
653 //
654 Status = MiDispatchFault(StoreInstruction,
655 Address,
656 PointerPte,
657 NULL,
658 FALSE,
659 NULL,
660 TrapInformation,
661 NULL);
662
663 /* Release the working set */
664 ASSERT(KeAreAllApcsDisabled() == TRUE);
665 MiUnlockWorkingSet(CurrentThread, WorkingSet);
666 KeLowerIrql(LockIrql);
667
668 //
669 // We are done!
670 //
671 DPRINT("Fault resolved with status: %lx\n", Status);
672 return Status;
673 }
674
675 /* This is a user fault */
676 CurrentThread = PsGetCurrentThread();
677 CurrentProcess = PsGetCurrentProcess();
678
679 /* Lock the working set */
680 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
681
682 #if (_MI_PAGING_LEVELS >= 3)
683 /* Need to check/handle PPE and PXE validity too */
684 ASSERT(FALSE);
685 #endif
686
687 /* First things first, is the PDE valid? */
688 ASSERT(PointerPde != MiAddressToPde(PTE_BASE));
689 ASSERT(PointerPde->u.Hard.LargePage == 0);
690 if (PointerPde->u.Hard.Valid == 0)
691 {
692 /* Right now, we only handle scenarios where the PDE is totally empty */
693 ASSERT(PointerPde->u.Long == 0);
694
695 /* Check if this address range belongs to a valid allocation (VAD) */
696 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
697
698 /* Right now, we expect a valid protection mask on the VAD */
699 ASSERT(ProtectionCode != MM_NOACCESS);
700
701 /* Make the PDE demand-zero */
702 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
703
704 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
705 Status = MiDispatchFault(TRUE,
706 PointerPte,
707 PointerPde,
708 NULL,
709 FALSE,
710 PsGetCurrentProcess(),
711 TrapInformation,
712 NULL);
713
714 /* We should come back with APCs enabled, and with a valid PDE */
715 ASSERT(KeAreAllApcsDisabled() == TRUE);
716 #if (_MI_PAGING_LEVELS >= 3)
717 /* Need to check/handle PPE and PXE validity too */
718 ASSERT(FALSE);
719 #endif
720 ASSERT(PointerPde->u.Hard.Valid == 1);
721 }
722
723 /* Now capture the PTE. We only handle cases where it's totally empty */
724 TempPte = *PointerPte;
725 ASSERT(TempPte.u.Long == 0);
726
727 /* Check if this address range belongs to a valid allocation (VAD) */
728 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
729 ASSERT(ProtectionCode != MM_NOACCESS);
730
731 /* Did we get a prototype PTE back? */
732 if (!ProtoPte)
733 {
734 /* No, create a new PTE. First, write the protection */
735 PointerPte->u.Soft.Protection = ProtectionCode;
736
737 /* Lock the PFN database since we're going to grab a page */
738 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
739
740 /* Grab a page out of there. Later we should grab a colored zero page */
741 PageFrameIndex = MiRemoveAnyPage(0);
742 ASSERT(PageFrameIndex);
743
744 /* Release the lock since we need to do some zeroing */
745 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
746
747 /* Zero out the page, since it's for user-mode */
748 MiZeroPfn(PageFrameIndex);
749
750 /* Grab the lock again so we can initialize the PFN entry */
751 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
752
753 /* Initialize the PFN entry now */
754 MiInitializePfn(PageFrameIndex, PointerPte, 1);
755
756 /* And we're done with the lock */
757 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
758
759 /* One more demand-zero fault */
760 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
761
762 /* Was the fault on an actual user page, or a kernel page for the user? */
763 if (PointerPte <= MiHighestUserPte)
764 {
765 /* User fault, build a user PTE */
766 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
767 PointerPte,
768 PointerPte->u.Soft.Protection,
769 PageFrameIndex);
770 }
771 else
772 {
773 /* Session, kernel, or user PTE, figure it out and build it */
774 MI_MAKE_HARDWARE_PTE(&TempPte,
775 PointerPte,
776 PointerPte->u.Soft.Protection,
777 PageFrameIndex);
778 }
779
780 /* Write the dirty bit for writeable pages */
781 if (TempPte.u.Hard.Write) TempPte.u.Hard.Dirty = TRUE;
782
783 /* And now write down the PTE, making the address valid */
784 MI_WRITE_VALID_PTE(PointerPte, TempPte);
785
786 /* Demand zero */
787 Status = STATUS_PAGE_FAULT_DEMAND_ZERO;
788 }
789 else
790 {
791 /* The only "prototype PTE" we support is the shared user data path */
792 ASSERT(ProtectionCode == MM_READONLY);
793
794 /* Write the prototype PTE */
795 TempPte = PrototypePte;
796 TempPte.u.Soft.Protection = ProtectionCode;
797 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
798
799 /* Handle the fault */
800 Status = MiDispatchFault(StoreInstruction,
801 Address,
802 PointerPte,
803 ProtoPte,
804 FALSE,
805 CurrentProcess,
806 TrapInformation,
807 Vad);
808 ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION);
809 ASSERT(PointerPte->u.Hard.Valid == 1);
810 ASSERT(PointerPte->u.Hard.PageFrameNumber == MmSharedUserDataPte->u.Hard.PageFrameNumber);
811 }
812
813 /* Release the working set */
814 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
815 return Status;
816 }
817
818 /* EOF */