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