[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)MM_SHARED_USER_DATA_VA)
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)
80 {
81 /* This must be a TEB/PEB VAD */
82 if (Vad->u.VadFlags.MemCommit)
83 {
84 /* It's committed, so return the VAD protection */
85 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
86 }
87 else
88 {
89 /* It has not yet been committed, so return no access */
90 *ProtectCode = MM_NOACCESS;
91 }
92 return NULL;
93 }
94 else
95 {
96 /* Return the proto VAD */
97 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
98 *ProtoVad = Vad;
99
100 /* Get the prototype PTE for this page */
101 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
102 ASSERT(PointerPte <= Vad->LastContiguousPte);
103 ASSERT(PointerPte != NULL);
104
105 /* Return the Prototype PTE and the protection for the page mapping */
106 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
107 return PointerPte;
108 }
109 }
110
111 #if (_MI_PAGING_LEVELS == 2)
112 BOOLEAN
113 FORCEINLINE
114 MiSynchronizeSystemPde(PMMPDE PointerPde)
115 {
116 MMPDE SystemPde;
117 ULONG Index;
118
119 /* Get the Index from the PDE */
120 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
121
122 /* Copy the PDE from the double-mapped system page directory */
123 SystemPde = MmSystemPagePtes[Index];
124 *PointerPde = SystemPde;
125
126 /* Make sure we re-read the PDE and PTE */
127 KeMemoryBarrierWithoutFence();
128
129 /* Return, if we had success */
130 return (BOOLEAN)SystemPde.u.Hard.Valid;
131 }
132
133 NTSTATUS
134 FASTCALL
135 MiCheckPdeForPagedPool(IN PVOID Address)
136 {
137 PMMPDE PointerPde;
138 NTSTATUS Status = STATUS_SUCCESS;
139
140 /* No session support in ReactOS yet */
141 ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE);
142 ASSERT(MI_IS_SESSION_PTE(Address) == FALSE);
143
144 //
145 // Check if this is a fault while trying to access the page table itself
146 //
147 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
148 {
149 //
150 // Send a hint to the page fault handler that this is only a valid fault
151 // if we already detected this was access within the page table range
152 //
153 PointerPde = (PMMPDE)MiAddressToPte(Address);
154 Status = STATUS_WAIT_1;
155 }
156 else if (Address < MmSystemRangeStart)
157 {
158 //
159 // This is totally illegal
160 //
161 return STATUS_ACCESS_VIOLATION;
162 }
163 else
164 {
165 //
166 // Get the PDE for the address
167 //
168 PointerPde = MiAddressToPde(Address);
169 }
170
171 //
172 // Check if it's not valid
173 //
174 if (PointerPde->u.Hard.Valid == 0)
175 {
176 #ifdef _M_AMD64
177 ASSERT(FALSE);
178 #else
179 //
180 // Copy it from our double-mapped system page directory
181 //
182 InterlockedExchangePte(PointerPde,
183 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
184 #endif
185 }
186
187 //
188 // Return status
189 //
190 return Status;
191 }
192 #else
193 NTSTATUS
194 FASTCALL
195 MiCheckPdeForPagedPool(IN PVOID Address)
196 {
197 return STATUS_ACCESS_VIOLATION;
198 }
199 #endif
200
201 VOID
202 NTAPI
203 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
204 {
205 PMMPTE ZeroPte;
206 MMPTE TempPte;
207 PMMPFN Pfn1;
208 PVOID ZeroAddress;
209
210 /* Get the PFN for this page */
211 Pfn1 = MiGetPfnEntry(PageFrameNumber);
212 ASSERT(Pfn1);
213
214 /* Grab a system PTE we can use to zero the page */
215 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
216 ASSERT(ZeroPte);
217
218 /* Initialize the PTE for it */
219 TempPte = ValidKernelPte;
220 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
221
222 /* Setup caching */
223 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
224 {
225 /* Write combining, no caching */
226 MI_PAGE_DISABLE_CACHE(&TempPte);
227 MI_PAGE_WRITE_COMBINED(&TempPte);
228 }
229 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
230 {
231 /* Write through, no caching */
232 MI_PAGE_DISABLE_CACHE(&TempPte);
233 MI_PAGE_WRITE_THROUGH(&TempPte);
234 }
235
236 /* Make the system PTE valid with our PFN */
237 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
238
239 /* Get the address it maps to, and zero it out */
240 ZeroAddress = MiPteToAddress(ZeroPte);
241 KeZeroPages(ZeroAddress, PAGE_SIZE);
242
243 /* Now get rid of it */
244 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
245 }
246
247 NTSTATUS
248 NTAPI
249 MiResolveDemandZeroFault(IN PVOID Address,
250 IN PMMPTE PointerPte,
251 IN PEPROCESS Process,
252 IN KIRQL OldIrql)
253 {
254 PFN_NUMBER PageFrameNumber = 0;
255 MMPTE TempPte;
256 BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
257 ULONG Color;
258 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
259 Address,
260 Process);
261
262 /* Must currently only be called by paging path */
263 if ((Process) && (OldIrql == MM_NOIRQL))
264 {
265 /* Sanity check */
266 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
267
268 /* No forking yet */
269 ASSERT(Process->ForkInProgress == NULL);
270
271 /* Get process color */
272 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
273 ASSERT(Color != 0xFFFFFFFF);
274
275 /* We'll need a zero page */
276 NeedZero = TRUE;
277 }
278 else
279 {
280 /* Check if we need a zero page */
281 NeedZero = (OldIrql != MM_NOIRQL);
282
283 /* Get the next system page color */
284 Color = MI_GET_NEXT_COLOR();
285 }
286
287 /* Check if the PFN database should be acquired */
288 if (OldIrql == MM_NOIRQL)
289 {
290 /* Acquire it and remember we should release it after */
291 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
292 HaveLock = TRUE;
293 }
294
295 /* We either manually locked the PFN DB, or already came with it locked */
296 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
297
298 /* Do we need a zero page? */
299 ASSERT(PointerPte->u.Hard.Valid == 0);
300 #if MI_TRACE_PFNS
301 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
302 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
303 #endif
304 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
305 if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
306 if ((NeedZero) && (Process))
307 {
308 /* Try to get one, if we couldn't grab a free page and zero it */
309 PageFrameNumber = MiRemoveZeroPageSafe(Color);
310 if (PageFrameNumber)
311 {
312 /* We got a genuine zero page, stop worrying about it */
313 NeedZero = FALSE;
314 }
315 else
316 {
317 /* We'll need a free page and zero it manually */
318 PageFrameNumber = MiRemoveAnyPage(Color);
319 }
320 }
321 else if (!NeedZero)
322 {
323 /* Process or system doesn't want a zero page, grab anything */
324 PageFrameNumber = MiRemoveAnyPage(Color);
325 }
326 else
327 {
328 /* System wants a zero page, obtain one */
329 PageFrameNumber = MiRemoveZeroPage(Color);
330 NeedZero = FALSE;
331 }
332
333 /* Initialize it */
334 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
335
336 /* Increment demand zero faults */
337 KeGetCurrentPrcb()->MmDemandZeroCount++;
338
339 /* Release PFN lock if needed */
340 if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
341
342 /* Zero the page if need be */
343 if (NeedZero) MiZeroPfn(PageFrameNumber);
344
345 /* Fault on user PDE, or fault on user PTE? */
346 if (PointerPte <= MiHighestUserPte)
347 {
348 /* User fault, build a user PTE */
349 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
350 PointerPte,
351 PointerPte->u.Soft.Protection,
352 PageFrameNumber);
353 }
354 else
355 {
356 /* This is a user-mode PDE, create a kernel PTE for it */
357 MI_MAKE_HARDWARE_PTE(&TempPte,
358 PointerPte,
359 PointerPte->u.Soft.Protection,
360 PageFrameNumber);
361 }
362
363 /* Set it dirty if it's a writable page */
364 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
365
366 /* Write it */
367 MI_WRITE_VALID_PTE(PointerPte, TempPte);
368
369 //
370 // It's all good now
371 //
372 DPRINT("Demand zero page has now been paged in\n");
373 return STATUS_PAGE_FAULT_DEMAND_ZERO;
374 }
375
376 NTSTATUS
377 NTAPI
378 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
379 IN PVOID Address,
380 IN PMMPTE PointerPte,
381 IN PMMPTE PointerProtoPte,
382 IN KIRQL OldIrql,
383 IN PMMPFN Pfn1)
384 {
385 MMPTE TempPte;
386 PMMPTE OriginalPte, PageTablePte;
387 ULONG_PTR Protection;
388 PFN_NUMBER PageFrameIndex;
389 PMMPFN Pfn2;
390
391 /* Must be called with an valid prototype PTE, with the PFN lock held */
392 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
393 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
394
395 /* Get the page */
396 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
397
398 /* Get the PFN entry and set it as a prototype PTE */
399 Pfn1 = MiGetPfnEntry(PageFrameIndex);
400 Pfn1->u3.e1.PrototypePte = 1;
401
402 /* Increment the share count for the page table */
403 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
404 // This could be beause MiDeletePte is now being called from strange code in Rosmm
405 PageTablePte = MiAddressToPte(PointerPte);
406 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
407 //Pfn2->u2.ShareCount++;
408
409 /* Check where we should be getting the protection information from */
410 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
411 {
412 /* Get the protection from the PTE, there's no real Proto PTE data */
413 Protection = PointerPte->u.Soft.Protection;
414 }
415 else
416 {
417 /* Get the protection from the original PTE link */
418 OriginalPte = &Pfn1->OriginalPte;
419 Protection = OriginalPte->u.Soft.Protection;
420 }
421
422 /* Release the PFN lock */
423 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
424
425 /* Remove caching bits */
426 Protection &= ~(MM_NOCACHE | MM_NOACCESS);
427
428 /* Check if this is a kernel or user address */
429 if (Address < MmSystemRangeStart)
430 {
431 /* Build the user PTE */
432 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
433 }
434 else
435 {
436 /* Build the kernel PTE */
437 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
438 }
439
440 /* Write the PTE */
441 MI_WRITE_VALID_PTE(PointerPte, TempPte);
442
443 /* Return success */
444 return STATUS_SUCCESS;
445 }
446
447 NTSTATUS
448 NTAPI
449 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
450 IN PVOID Address,
451 IN PMMPTE PointerPte,
452 IN PMMPTE PointerProtoPte,
453 IN OUT PMMPFN *OutPfn,
454 OUT PVOID *PageFileData,
455 OUT PMMPTE PteValue,
456 IN PEPROCESS Process,
457 IN KIRQL OldIrql,
458 IN PVOID TrapInformation)
459 {
460 MMPTE TempPte;
461 PMMPFN Pfn1;
462 PFN_NUMBER PageFrameIndex;
463 NTSTATUS Status;
464
465 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
466 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
467 ASSERT(PointerPte->u.Hard.Valid == 0);
468 ASSERT(PointerPte->u.Soft.Prototype == 1);
469
470 /* Read the prototype PTE and check if it's valid */
471 TempPte = *PointerProtoPte;
472 if (TempPte.u.Hard.Valid == 1)
473 {
474 /* One more user of this mapped page */
475 PageFrameIndex = PFN_FROM_PTE(&TempPte);
476 Pfn1 = MiGetPfnEntry(PageFrameIndex);
477 Pfn1->u2.ShareCount++;
478
479 /* Call it a transition */
480 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
481
482 /* Complete the prototype PTE fault -- this will release the PFN lock */
483 return MiCompleteProtoPteFault(StoreInstruction,
484 Address,
485 PointerPte,
486 PointerProtoPte,
487 OldIrql,
488 NULL);
489 }
490
491 /* Make sure there's some protection mask */
492 if (TempPte.u.Long == 0)
493 {
494 /* Release the lock */
495 DPRINT1("Access on reserved section?\n");
496 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
497 return STATUS_ACCESS_VIOLATION;
498 }
499
500 /* This is the only thing we support right now */
501 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
502 ASSERT(TempPte.u.Proto.ReadOnly == 0);
503 ASSERT(PointerPte > MiHighestUserPte);
504 ASSERT(TempPte.u.Soft.Prototype == 0);
505 ASSERT(TempPte.u.Soft.Transition == 0);
506
507 /* Resolve the demand zero fault */
508 Status = MiResolveDemandZeroFault(Address,
509 PointerProtoPte,
510 Process,
511 OldIrql);
512 ASSERT(NT_SUCCESS(Status));
513
514 /* Complete the prototype PTE fault -- this will release the PFN lock */
515 ASSERT(PointerPte->u.Hard.Valid == 0);
516 return MiCompleteProtoPteFault(StoreInstruction,
517 Address,
518 PointerPte,
519 PointerProtoPte,
520 OldIrql,
521 NULL);
522 }
523
524 NTSTATUS
525 NTAPI
526 MiDispatchFault(IN BOOLEAN StoreInstruction,
527 IN PVOID Address,
528 IN PMMPTE PointerPte,
529 IN PMMPTE PointerProtoPte,
530 IN BOOLEAN Recursive,
531 IN PEPROCESS Process,
532 IN PVOID TrapInformation,
533 IN PVOID Vad)
534 {
535 MMPTE TempPte;
536 KIRQL OldIrql, LockIrql;
537 NTSTATUS Status;
538 PMMPTE SuperProtoPte;
539 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
540 Address,
541 Process);
542
543 /* Make sure the addresses are ok */
544 ASSERT(PointerPte == MiAddressToPte(Address));
545
546 //
547 // Make sure APCs are off and we're not at dispatch
548 //
549 OldIrql = KeGetCurrentIrql();
550 ASSERT(OldIrql <= APC_LEVEL);
551 ASSERT(KeAreAllApcsDisabled() == TRUE);
552
553 //
554 // Grab a copy of the PTE
555 //
556 TempPte = *PointerPte;
557
558 /* Do we have a prototype PTE? */
559 if (PointerProtoPte)
560 {
561 /* This should never happen */
562 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
563
564 /* Check if this is a kernel-mode address */
565 SuperProtoPte = MiAddressToPte(PointerProtoPte);
566 if (Address >= MmSystemRangeStart)
567 {
568 /* Lock the PFN database */
569 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
570
571 /* Has the PTE been made valid yet? */
572 if (!SuperProtoPte->u.Hard.Valid)
573 {
574 UNIMPLEMENTED;
575 while (TRUE);
576 }
577 else
578 {
579 /* Resolve the fault -- this will release the PFN lock */
580 ASSERT(PointerPte->u.Hard.Valid == 0);
581 Status = MiResolveProtoPteFault(StoreInstruction,
582 Address,
583 PointerPte,
584 PointerProtoPte,
585 NULL,
586 NULL,
587 NULL,
588 Process,
589 LockIrql,
590 TrapInformation);
591 ASSERT(Status == STATUS_SUCCESS);
592
593 /* Complete this as a transition fault */
594 ASSERT(OldIrql == KeGetCurrentIrql());
595 ASSERT(OldIrql <= APC_LEVEL);
596 ASSERT(KeAreAllApcsDisabled() == TRUE);
597 return Status;
598 }
599 }
600 else
601 {
602 /* We currently only handle very limited paths */
603 ASSERT(PointerPte->u.Soft.Prototype == 1);
604 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
605
606 /* Lock the PFN database */
607 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
608
609 /* For our current usage, this should be true */
610 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
611 ASSERT(TempPte.u.Hard.Valid == 0);
612
613 /* Resolve the fault -- this will release the PFN lock */
614 Status = MiResolveProtoPteFault(StoreInstruction,
615 Address,
616 PointerPte,
617 PointerProtoPte,
618 NULL,
619 NULL,
620 NULL,
621 Process,
622 LockIrql,
623 TrapInformation);
624 ASSERT(Status == STATUS_SUCCESS);
625
626 /* Complete this as a transition fault */
627 ASSERT(OldIrql == KeGetCurrentIrql());
628 ASSERT(OldIrql <= APC_LEVEL);
629 ASSERT(KeAreAllApcsDisabled() == TRUE);
630 return STATUS_PAGE_FAULT_TRANSITION;
631 }
632 }
633
634 //
635 // The PTE must be invalid but not completely empty. It must also not be a
636 // prototype PTE as that scenario should've been handled above
637 //
638 ASSERT(TempPte.u.Hard.Valid == 0);
639 ASSERT(TempPte.u.Soft.Prototype == 0);
640 ASSERT(TempPte.u.Long != 0);
641
642 //
643 // No transition or page file software PTEs in ARM3 yet, so this must be a
644 // demand zero page
645 //
646 ASSERT(TempPte.u.Soft.Transition == 0);
647 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
648
649 //
650 // If we got this far, the PTE can only be a demand zero PTE, which is what
651 // we want. Go handle it!
652 //
653 Status = MiResolveDemandZeroFault(Address,
654 PointerPte,
655 Process,
656 MM_NOIRQL);
657 ASSERT(KeAreAllApcsDisabled() == TRUE);
658 if (NT_SUCCESS(Status))
659 {
660 //
661 // Make sure we're returning in a sane state and pass the status down
662 //
663 ASSERT(OldIrql == KeGetCurrentIrql());
664 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
665 return Status;
666 }
667
668 //
669 // Generate an access fault
670 //
671 return STATUS_ACCESS_VIOLATION;
672 }
673
674 NTSTATUS
675 NTAPI
676 MmArmAccessFault(IN BOOLEAN StoreInstruction,
677 IN PVOID Address,
678 IN KPROCESSOR_MODE Mode,
679 IN PVOID TrapInformation)
680 {
681 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
682 PMMPTE ProtoPte = NULL;
683 PMMPTE PointerPte = MiAddressToPte(Address);
684 PMMPDE PointerPde = MiAddressToPde(Address);
685 #if (_MI_PAGING_LEVELS >= 3)
686 PMMPDE PointerPpe = MiAddressToPpe(Address);
687 #if (_MI_PAGING_LEVELS == 4)
688 PMMPDE PointerPxe = MiAddressToPxe(Address);
689 #endif
690 #endif
691 MMPTE TempPte;
692 PETHREAD CurrentThread;
693 PEPROCESS CurrentProcess;
694 NTSTATUS Status;
695 PMMSUPPORT WorkingSet;
696 ULONG ProtectionCode;
697 PMMVAD Vad;
698 PFN_NUMBER PageFrameIndex;
699 ULONG Color;
700 DPRINT("ARM3 FAULT AT: %p\n", Address);
701
702 /* Check for page fault on high IRQL */
703 if (OldIrql > APC_LEVEL)
704 {
705 // There are some special cases where this is okay, but not in ARM3 yet
706 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
707 Address,
708 OldIrql);
709 ASSERT(OldIrql <= APC_LEVEL);
710 }
711
712 /* Check for kernel fault address */
713 if (Address >= MmSystemRangeStart)
714 {
715 /* Bail out, if the fault came from user mode */
716 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
717
718 /* PXEs and PPEs for kernel mode are mapped for everything we need */
719 #if (_MI_PAGING_LEVELS >= 3)
720 if (
721 #if (_MI_PAGING_LEVELS == 4)
722 (PointerPxe->u.Hard.Valid == 0) ||
723 #endif
724 (PointerPpe->u.Hard.Valid == 0))
725 {
726 /* The address is not from any pageable area! */
727 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
728 (ULONG_PTR)Address,
729 StoreInstruction,
730 (ULONG_PTR)TrapInformation,
731 2);
732 }
733 #endif
734
735 #if (_MI_PAGING_LEVELS == 2)
736 /* Check if we have a situation that might need synchronization
737 of the PDE with the system page directory */
738 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
739 {
740 /* This could be a paged pool commit with an unsychronized PDE.
741 NOTE: This way it works on x86, verify for other architectures! */
742 if (MiSynchronizeSystemPde((PMMPDE)PointerPte)) return STATUS_SUCCESS;
743 }
744 #endif
745
746 /* Check if the PDE is invalid */
747 if (PointerPde->u.Hard.Valid == 0)
748 {
749 #if (_MI_PAGING_LEVELS == 2)
750 /* Sync this PDE and check, if that made it valid */
751 if (!MiSynchronizeSystemPde(PointerPde))
752 #endif
753 {
754 /* PDE (still) not valid, kill the system */
755 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
756 (ULONG_PTR)Address,
757 StoreInstruction,
758 (ULONG_PTR)TrapInformation,
759 2);
760 }
761 }
762
763 /* The PDE is valid, so read the PTE */
764 TempPte = *PointerPte;
765 if (TempPte.u.Hard.Valid == 1)
766 {
767 //
768 // Only two things can go wrong here:
769 // Executing NX page (we couldn't care less)
770 // Writing to a read-only page (the stuff ARM3 works with is write,
771 // so again, moot point).
772 //
773
774 //
775 // Otherwise, the PDE was probably invalid, and all is good now
776 //
777 return STATUS_SUCCESS;
778 }
779
780 /* Get the current thread */
781 CurrentThread = PsGetCurrentThread();
782
783 /* Check for a fault on the page table or hyperspace */
784 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address)) goto UserFault;
785
786 /* Use the system working set */
787 WorkingSet = &MmSystemCacheWs;
788 CurrentProcess = NULL;
789
790 /* Acquire the working set lock */
791 KeRaiseIrql(APC_LEVEL, &LockIrql);
792 MiLockWorkingSet(CurrentThread, WorkingSet);
793
794 /* Re-read PTE now that we own the lock */
795 TempPte = *PointerPte;
796 if (TempPte.u.Hard.Valid == 1)
797 {
798 // Only two things can go wrong here:
799 // Executing NX page (we couldn't care less)
800 // Writing to a read-only page (the stuff ARM3 works with is write,
801 // so again, moot point).
802 ASSERT(TempPte.u.Hard.Write == 1);
803
804 /* Release the working set */
805 MiUnlockWorkingSet(CurrentThread, WorkingSet);
806 KeLowerIrql(LockIrql);
807
808 // Otherwise, the PDE was probably invalid, and all is good now
809 return STATUS_SUCCESS;
810 }
811
812 /* Check one kind of prototype PTE */
813 if (TempPte.u.Soft.Prototype)
814 {
815 /* Make sure protected pool is on, and that this is a pool address */
816 if ((MmProtectFreedNonPagedPool) &&
817 (((Address >= MmNonPagedPoolStart) &&
818 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
819 MmSizeOfNonPagedPoolInBytes))) ||
820 ((Address >= MmNonPagedPoolExpansionStart) &&
821 (Address < MmNonPagedPoolEnd))))
822 {
823 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
824 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
825 (ULONG_PTR)Address,
826 StoreInstruction,
827 Mode,
828 4);
829 }
830
831 /* Get the prototype PTE! */
832 ProtoPte = MiProtoPteToPte(&TempPte);
833 }
834 else
835 {
836 /* We don't implement transition PTEs */
837 ASSERT(TempPte.u.Soft.Transition == 0);
838
839 /* Check for no-access PTE */
840 if (TempPte.u.Soft.Protection == MM_NOACCESS)
841 {
842 /* Bugcheck the system! */
843 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
844 (ULONG_PTR)Address,
845 StoreInstruction,
846 (ULONG_PTR)TrapInformation,
847 1);
848 }
849
850 /* Check for demand page */
851 if ((StoreInstruction) && !(TempPte.u.Hard.Valid))
852 {
853 /* Get the protection code */
854 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
855 {
856 /* Bugcheck the system! */
857 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
858 (ULONG_PTR)Address,
859 TempPte.u.Long,
860 (ULONG_PTR)TrapInformation,
861 14);
862 }
863 }
864 }
865
866 /* Now do the real fault handling */
867 Status = MiDispatchFault(StoreInstruction,
868 Address,
869 PointerPte,
870 ProtoPte,
871 FALSE,
872 CurrentProcess,
873 TrapInformation,
874 NULL);
875
876 /* Release the working set */
877 ASSERT(KeAreAllApcsDisabled() == TRUE);
878 MiUnlockWorkingSet(CurrentThread, WorkingSet);
879 KeLowerIrql(LockIrql);
880
881 /* We are done! */
882 DPRINT("Fault resolved with status: %lx\n", Status);
883 return Status;
884 }
885
886 /* This is a user fault */
887 UserFault:
888 CurrentThread = PsGetCurrentThread();
889 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
890
891 /* Lock the working set */
892 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
893
894 #if (_MI_PAGING_LEVELS == 2)
895 ASSERT(PointerPde->u.Hard.LargePage == 0);
896 #endif
897
898 #if (_MI_PAGING_LEVELS == 4)
899 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
900 // also this is missing the page count increment
901 /* Check if the PXE is valid */
902 if (PointerPxe->u.Hard.Valid == 0)
903 {
904 /* Right now, we only handle scenarios where the PXE is totally empty */
905 ASSERT(PointerPxe->u.Long == 0);
906 #if 0
907 /* Resolve a demand zero fault */
908 Status = MiResolveDemandZeroFault(PointerPpe,
909 MM_READWRITE,
910 CurrentProcess,
911 MM_NOIRQL);
912 #endif
913 /* We should come back with a valid PXE */
914 ASSERT(PointerPxe->u.Hard.Valid == 1);
915 }
916 #endif
917
918 #if (_MI_PAGING_LEVELS >= 3)
919 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
920 // also this is missing the page count increment
921 /* Check if the PPE is valid */
922 if (PointerPpe->u.Hard.Valid == 0)
923 {
924 /* Right now, we only handle scenarios where the PPE is totally empty */
925 ASSERT(PointerPpe->u.Long == 0);
926 #if 0
927 /* Resolve a demand zero fault */
928 Status = MiResolveDemandZeroFault(PointerPde,
929 MM_READWRITE,
930 CurrentProcess,
931 MM_NOIRQL);
932 #endif
933 /* We should come back with a valid PPE */
934 ASSERT(PointerPpe->u.Hard.Valid == 1);
935 }
936 #endif
937
938 /* Check if the PDE is valid */
939 if (PointerPde->u.Hard.Valid == 0)
940 {
941 /* Right now, we only handle scenarios where the PDE is totally empty */
942 ASSERT(PointerPde->u.Long == 0);
943
944 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
945 #if MI_TRACE_PFNS
946 UserPdeFault = TRUE;
947 #endif
948 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
949 if (ProtectionCode == MM_NOACCESS)
950 {
951 #if (_MI_PAGING_LEVELS == 2)
952 /* Could be a page table for paged pool */
953 MiCheckPdeForPagedPool(Address);
954 #endif
955 /* Has the code above changed anything -- is this now a valid PTE? */
956 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
957
958 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
959 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
960 return Status;
961 }
962
963 /* Write a demand-zero PDE */
964 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
965
966 /* Dispatch the fault */
967 Status = MiDispatchFault(TRUE,
968 PointerPte,
969 PointerPde,
970 NULL,
971 FALSE,
972 PsGetCurrentProcess(),
973 TrapInformation,
974 NULL);
975 #if MI_TRACE_PFNS
976 UserPdeFault = FALSE;
977 #endif
978 /* We should come back with APCs enabled, and with a valid PDE */
979 ASSERT(KeAreAllApcsDisabled() == TRUE);
980 ASSERT(PointerPde->u.Hard.Valid == 1);
981 }
982
983 /* Now capture the PTE. Ignore virtual faults for now */
984 TempPte = *PointerPte;
985 ASSERT(TempPte.u.Hard.Valid == 0);
986
987 /* Quick check for demand-zero */
988 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
989 {
990 /* Resolve the fault */
991 MiResolveDemandZeroFault(Address,
992 PointerPte,
993 CurrentProcess,
994 MM_NOIRQL);
995
996 /* Return the status */
997 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
998 return STATUS_PAGE_FAULT_DEMAND_ZERO;
999 }
1000
1001 /* Make sure it's not a prototype PTE */
1002 ASSERT(TempPte.u.Soft.Prototype == 0);
1003
1004 /* Check if this address range belongs to a valid allocation (VAD) */
1005 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1006 if (ProtectionCode == MM_NOACCESS)
1007 {
1008 #if (_MI_PAGING_LEVELS == 2)
1009 /* Could be a page table for paged pool */
1010 MiCheckPdeForPagedPool(Address);
1011 #endif
1012 /* Has the code above changed anything -- is this now a valid PTE? */
1013 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1014
1015 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1016 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1017 return Status;
1018 }
1019
1020 /* Check for non-demand zero PTE */
1021 if (TempPte.u.Long != 0)
1022 {
1023 /* This is a page fault */
1024
1025 /* FIXME: Run MiAccessCheck */
1026
1027 /* Dispatch the fault */
1028 Status = MiDispatchFault(StoreInstruction,
1029 Address,
1030 PointerPte,
1031 NULL,
1032 FALSE,
1033 PsGetCurrentProcess(),
1034 TrapInformation,
1035 NULL);
1036
1037 /* Return the status */
1038 ASSERT(NT_SUCCESS(Status));
1039 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1040 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1041 return Status;
1042 }
1043
1044 /*
1045 * Check if this is a real user-mode address or actually a kernel-mode
1046 * page table for a user mode address
1047 */
1048 if (Address <= MM_HIGHEST_USER_ADDRESS)
1049 {
1050 /* Add an additional page table reference */
1051 MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
1052 ASSERT(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
1053 }
1054
1055 /* No guard page support yet */
1056 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1057
1058 /* Did we get a prototype PTE back? */
1059 if (!ProtoPte)
1060 {
1061 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1062 if (PointerPde == MiAddressToPde(PTE_BASE))
1063 {
1064 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1065 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1066 }
1067 else
1068 {
1069 /* No, create a new PTE. First, write the protection */
1070 PointerPte->u.Soft.Protection = ProtectionCode;
1071 }
1072
1073 /* Lock the PFN database since we're going to grab a page */
1074 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1075
1076 /* Try to get a zero page */
1077 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1078 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1079 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1080 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1081 if (!PageFrameIndex)
1082 {
1083 /* Grab a page out of there. Later we should grab a colored zero page */
1084 PageFrameIndex = MiRemoveAnyPage(Color);
1085 ASSERT(PageFrameIndex);
1086
1087 /* Release the lock since we need to do some zeroing */
1088 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1089
1090 /* Zero out the page, since it's for user-mode */
1091 MiZeroPfn(PageFrameIndex);
1092
1093 /* Grab the lock again so we can initialize the PFN entry */
1094 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1095 }
1096
1097 /* Initialize the PFN entry now */
1098 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1099
1100 /* One more demand-zero fault */
1101 KeGetCurrentPrcb()->MmDemandZeroCount++;
1102
1103 /* And we're done with the lock */
1104 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1105
1106 /* Fault on user PDE, or fault on user PTE? */
1107 if (PointerPte <= MiHighestUserPte)
1108 {
1109 /* User fault, build a user PTE */
1110 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1111 PointerPte,
1112 PointerPte->u.Soft.Protection,
1113 PageFrameIndex);
1114 }
1115 else
1116 {
1117 /* This is a user-mode PDE, create a kernel PTE for it */
1118 MI_MAKE_HARDWARE_PTE(&TempPte,
1119 PointerPte,
1120 PointerPte->u.Soft.Protection,
1121 PageFrameIndex);
1122 }
1123
1124 /* Write the dirty bit for writeable pages */
1125 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1126
1127 /* And now write down the PTE, making the address valid */
1128 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1129 ASSERT(MiGetPfnEntry(PageFrameIndex)->u1.Event == NULL);
1130
1131 /* Demand zero */
1132 Status = STATUS_PAGE_FAULT_DEMAND_ZERO;
1133 }
1134 else
1135 {
1136 /* No guard page support yet */
1137 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1138 ASSERT(ProtectionCode != 0x100);
1139
1140 /* Write the prototype PTE */
1141 TempPte = PrototypePte;
1142 TempPte.u.Soft.Protection = ProtectionCode;
1143 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1144
1145 /* Handle the fault */
1146 Status = MiDispatchFault(StoreInstruction,
1147 Address,
1148 PointerPte,
1149 ProtoPte,
1150 FALSE,
1151 CurrentProcess,
1152 TrapInformation,
1153 Vad);
1154 ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION);
1155 ASSERT(PointerPte->u.Hard.Valid == 1);
1156 ASSERT(PointerPte->u.Hard.PageFrameNumber != 0);
1157 }
1158
1159 /* Release the working set */
1160 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1161 return Status;
1162 }
1163
1164 NTSTATUS
1165 NTAPI
1166 MmGetExecuteOptions(IN PULONG ExecuteOptions)
1167 {
1168 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1169 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1170
1171 *ExecuteOptions = 0;
1172
1173 if (CurrentProcess->Flags.ExecuteDisable)
1174 {
1175 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
1176 }
1177
1178 if (CurrentProcess->Flags.ExecuteEnable)
1179 {
1180 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
1181 }
1182
1183 if (CurrentProcess->Flags.DisableThunkEmulation)
1184 {
1185 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
1186 }
1187
1188 if (CurrentProcess->Flags.Permanent)
1189 {
1190 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
1191 }
1192
1193 if (CurrentProcess->Flags.ExecuteDispatchEnable)
1194 {
1195 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
1196 }
1197
1198 if (CurrentProcess->Flags.ImageDispatchEnable)
1199 {
1200 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
1201 }
1202
1203 return STATUS_SUCCESS;
1204 }
1205
1206 NTSTATUS
1207 NTAPI
1208 MmSetExecuteOptions(IN ULONG ExecuteOptions)
1209 {
1210 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1211 KLOCK_QUEUE_HANDLE ProcessLock;
1212 NTSTATUS Status = STATUS_ACCESS_DENIED;
1213 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1214
1215 /* Only accept valid flags */
1216 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
1217 {
1218 /* Fail */
1219 DPRINT1("Invalid no-execute options\n");
1220 return STATUS_INVALID_PARAMETER;
1221 }
1222
1223 /* Change the NX state in the process lock */
1224 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
1225
1226 /* Don't change anything if the permanent flag was set */
1227 if (!CurrentProcess->Flags.Permanent)
1228 {
1229 /* Start by assuming it's not disabled */
1230 CurrentProcess->Flags.ExecuteDisable = FALSE;
1231
1232 /* Now process each flag and turn the equivalent bit on */
1233 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
1234 {
1235 CurrentProcess->Flags.ExecuteDisable = TRUE;
1236 }
1237 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
1238 {
1239 CurrentProcess->Flags.ExecuteEnable = TRUE;
1240 }
1241 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
1242 {
1243 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
1244 }
1245 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
1246 {
1247 CurrentProcess->Flags.Permanent = TRUE;
1248 }
1249 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
1250 {
1251 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1252 }
1253 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
1254 {
1255 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1256 }
1257
1258 /* These are turned on by default if no-execution is also eanbled */
1259 if (CurrentProcess->Flags.ExecuteEnable)
1260 {
1261 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1262 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1263 }
1264
1265 /* All good */
1266 Status = STATUS_SUCCESS;
1267 }
1268
1269 /* Release the lock and return status */
1270 KiReleaseProcessLock(&ProcessLock);
1271 return Status;
1272 }
1273
1274 /* EOF */