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