[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 #define HYDRA_PROCESS (PEPROCESS)1
21 #if MI_TRACE_PFNS
22 BOOLEAN UserPdeFault = FALSE;
23 #endif
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 NTSTATUS
28 NTAPI
29 MiCheckForUserStackOverflow(IN PVOID Address,
30 IN PVOID TrapInformation)
31 {
32 PETHREAD CurrentThread = PsGetCurrentThread();
33 PTEB Teb = CurrentThread->Tcb.Teb;
34 PVOID StackBase, DeallocationStack, NextStackAddress;
35 SIZE_T GuranteedSize;
36 NTSTATUS Status;
37
38 /* Do we own the address space lock? */
39 if (CurrentThread->AddressSpaceOwner == 1)
40 {
41 /* This isn't valid */
42 DPRINT1("Process owns address space lock\n");
43 ASSERT(KeAreAllApcsDisabled() == TRUE);
44 return STATUS_GUARD_PAGE_VIOLATION;
45 }
46
47 /* Are we attached? */
48 if (KeIsAttachedProcess())
49 {
50 /* This isn't valid */
51 DPRINT1("Process is attached\n");
52 return STATUS_GUARD_PAGE_VIOLATION;
53 }
54
55 /* Read the current settings */
56 StackBase = Teb->NtTib.StackBase;
57 DeallocationStack = Teb->DeallocationStack;
58 GuranteedSize = Teb->GuaranteedStackBytes;
59 DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
60 StackBase, DeallocationStack, GuranteedSize);
61
62 /* Guarantees make this code harder, for now, assume there aren't any */
63 ASSERT(GuranteedSize == 0);
64
65 /* So allocate only the minimum guard page size */
66 GuranteedSize = PAGE_SIZE;
67
68 /* Does this faulting stack address actually exist in the stack? */
69 if ((Address >= StackBase) || (Address < DeallocationStack))
70 {
71 /* That's odd... */
72 DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
73 Address, StackBase, DeallocationStack);
74 return STATUS_GUARD_PAGE_VIOLATION;
75 }
76
77 /* This is where the stack will start now */
78 NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(Address) - GuranteedSize);
79
80 /* Do we have at least one page between here and the end of the stack? */
81 if (((ULONG_PTR)NextStackAddress - PAGE_SIZE) <= (ULONG_PTR)DeallocationStack)
82 {
83 /* We don't -- Windows would try to make this guard page valid now */
84 DPRINT1("Close to our death...\n");
85 ASSERT(FALSE);
86 return STATUS_STACK_OVERFLOW;
87 }
88
89 /* Don't handle this flag yet */
90 ASSERT((PsGetCurrentProcess()->Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) == 0);
91
92 /* Update the stack limit */
93 Teb->NtTib.StackLimit = (PVOID)((ULONG_PTR)NextStackAddress + GuranteedSize);
94
95 /* Now move the guard page to the next page */
96 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
97 &NextStackAddress,
98 0,
99 &GuranteedSize,
100 MEM_COMMIT,
101 PAGE_READWRITE | PAGE_GUARD);
102 if ((NT_SUCCESS(Status) || (Status == STATUS_ALREADY_COMMITTED)))
103 {
104 /* We did it! */
105 DPRINT("Guard page handled successfully for %p\n", Address);
106 return STATUS_PAGE_FAULT_GUARD_PAGE;
107 }
108
109 /* Fail, we couldn't move the guard page */
110 DPRINT1("Guard page failure: %lx\n", Status);
111 ASSERT(FALSE);
112 return STATUS_STACK_OVERFLOW;
113 }
114
115 FORCEINLINE
116 BOOLEAN
117 MiIsAccessAllowed(
118 _In_ ULONG ProtectionMask,
119 _In_ BOOLEAN Write,
120 _In_ BOOLEAN Execute)
121 {
122 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
123 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
124 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
125 static const UCHAR MiAccessAllowedMask[2][2] =
126 {
127 { // Protect 0 1 2 3 4 5 6 7
128 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
129 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
130 },
131 {
132 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
133 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
134 }
135 };
136
137 /* We want only the low 3 bits */
138 ProtectionMask &= 7;
139
140 /* Look it up in the table */
141 return (MiAccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1;
142 }
143
144 NTSTATUS
145 NTAPI
146 MiAccessCheck(IN PMMPTE PointerPte,
147 IN BOOLEAN StoreInstruction,
148 IN KPROCESSOR_MODE PreviousMode,
149 IN ULONG_PTR ProtectionMask,
150 IN PVOID TrapFrame,
151 IN BOOLEAN LockHeld)
152 {
153 MMPTE TempPte;
154
155 /* Check for invalid user-mode access */
156 if ((PreviousMode == UserMode) && (PointerPte > MiHighestUserPte))
157 {
158 return STATUS_ACCESS_VIOLATION;
159 }
160
161 /* Capture the PTE -- is it valid? */
162 TempPte = *PointerPte;
163 if (TempPte.u.Hard.Valid)
164 {
165 /* Was someone trying to write to it? */
166 if (StoreInstruction)
167 {
168 /* Is it writable?*/
169 if ((TempPte.u.Hard.Write) || (TempPte.u.Hard.CopyOnWrite))
170 {
171 /* Then there's nothing to worry about */
172 return STATUS_SUCCESS;
173 }
174
175 /* Oops! This isn't allowed */
176 return STATUS_ACCESS_VIOLATION;
177 }
178
179 /* Someone was trying to read from a valid PTE, that's fine too */
180 return STATUS_SUCCESS;
181 }
182
183 /* Check if the protection on the page allows what is being attempted */
184 if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE))
185 {
186 return STATUS_ACCESS_VIOLATION;
187 }
188
189 /* Check if this is a guard page */
190 if (ProtectionMask & MM_GUARDPAGE)
191 {
192 NT_ASSERT(ProtectionMask != MM_DECOMMIT);
193
194 /* Attached processes can't expand their stack */
195 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION;
196
197 /* No support for transition PTEs yet */
198 ASSERT(((TempPte.u.Soft.Transition == 1) &&
199 (TempPte.u.Soft.Prototype == 0)) == FALSE);
200
201 /* Remove the guard page bit, and return a guard page violation */
202 TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE;
203 NT_ASSERT(TempPte.u.Long != 0);
204 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
205 return STATUS_GUARD_PAGE_VIOLATION;
206 }
207
208 /* Nothing to do */
209 return STATUS_SUCCESS;
210 }
211
212 PMMPTE
213 NTAPI
214 MiCheckVirtualAddress(IN PVOID VirtualAddress,
215 OUT PULONG ProtectCode,
216 OUT PMMVAD *ProtoVad)
217 {
218 PMMVAD Vad;
219 PMMPTE PointerPte;
220
221 /* No prototype/section support for now */
222 *ProtoVad = NULL;
223
224 /* User or kernel fault? */
225 if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS)
226 {
227 /* Special case for shared data */
228 if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
229 {
230 /* It's a read-only page */
231 *ProtectCode = MM_READONLY;
232 return MmSharedUserDataPte;
233 }
234
235 /* Find the VAD, it might not exist if the address is bogus */
236 Vad = MiLocateAddress(VirtualAddress);
237 if (!Vad)
238 {
239 /* Bogus virtual address */
240 *ProtectCode = MM_NOACCESS;
241 return NULL;
242 }
243
244 /* ReactOS does not handle physical memory VADs yet */
245 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
246
247 /* Check if it's a section, or just an allocation */
248 if (Vad->u.VadFlags.PrivateMemory)
249 {
250 /* ReactOS does not handle AWE VADs yet */
251 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
252
253 /* This must be a TEB/PEB VAD */
254 if (Vad->u.VadFlags.MemCommit)
255 {
256 /* It's committed, so return the VAD protection */
257 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
258 }
259 else
260 {
261 /* It has not yet been committed, so return no access */
262 *ProtectCode = MM_NOACCESS;
263 }
264
265 /* In both cases, return no PTE */
266 return NULL;
267 }
268 else
269 {
270 /* ReactOS does not supoprt these VADs yet */
271 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
272 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
273
274 /* Return the proto VAD */
275 *ProtoVad = Vad;
276
277 /* Get the prototype PTE for this page */
278 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
279 ASSERT(PointerPte != NULL);
280 ASSERT(PointerPte <= Vad->LastContiguousPte);
281
282 /* Return the Prototype PTE and the protection for the page mapping */
283 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
284 return PointerPte;
285 }
286 }
287 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
288 {
289 /* This should never happen, as these addresses are handled by the double-maping */
290 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
291 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
292 {
293 /* Fail such access */
294 *ProtectCode = MM_NOACCESS;
295 return NULL;
296 }
297
298 /* Return full access rights */
299 *ProtectCode = MM_READWRITE;
300 return NULL;
301 }
302 else if (MI_IS_SESSION_ADDRESS(VirtualAddress))
303 {
304 /* ReactOS does not have an image list yet, so bail out to failure case */
305 ASSERT(IsListEmpty(&MmSessionSpace->ImageList));
306 }
307
308 /* Default case -- failure */
309 *ProtectCode = MM_NOACCESS;
310 return NULL;
311 }
312
313 #if (_MI_PAGING_LEVELS == 2)
314 BOOLEAN
315 FORCEINLINE
316 MiSynchronizeSystemPde(PMMPDE PointerPde)
317 {
318 MMPDE SystemPde;
319 ULONG Index;
320
321 /* Get the Index from the PDE */
322 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
323
324 /* Copy the PDE from the double-mapped system page directory */
325 SystemPde = MmSystemPagePtes[Index];
326 *PointerPde = SystemPde;
327
328 /* Make sure we re-read the PDE and PTE */
329 KeMemoryBarrierWithoutFence();
330
331 /* Return, if we had success */
332 return (BOOLEAN)SystemPde.u.Hard.Valid;
333 }
334
335 NTSTATUS
336 FASTCALL
337 MiCheckPdeForSessionSpace(IN PVOID Address)
338 {
339 MMPTE TempPde;
340 PMMPTE PointerPde;
341 PVOID SessionAddress;
342 ULONG Index;
343
344 /* Is this a session PTE? */
345 if (MI_IS_SESSION_PTE(Address))
346 {
347 /* Make sure the PDE for session space is valid */
348 PointerPde = MiAddressToPde(MmSessionSpace);
349 if (!PointerPde->u.Hard.Valid)
350 {
351 /* This means there's no valid session, bail out */
352 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
353 Address);
354 DbgBreakPoint();
355 return STATUS_ACCESS_VIOLATION;
356 }
357
358 /* Now get the session-specific page table for this address */
359 SessionAddress = MiPteToAddress(Address);
360 PointerPde = MiAddressToPte(Address);
361 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
362
363 /* It's not valid, so find it in the page table array */
364 Index = ((ULONG_PTR)SessionAddress - (ULONG_PTR)MmSessionBase) >> 22;
365 TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
366 if (TempPde.u.Hard.Valid)
367 {
368 /* The copy is valid, so swap it in */
369 InterlockedExchange((PLONG)PointerPde, TempPde.u.Long);
370 return STATUS_WAIT_1;
371 }
372
373 /* We don't seem to have allocated a page table for this address yet? */
374 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
375 PointerPde->u.Long, SessionAddress);
376 DbgBreakPoint();
377 return STATUS_ACCESS_VIOLATION;
378 }
379
380 /* Is the address also a session address? If not, we're done */
381 if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS;
382
383 /* It is, so again get the PDE for session space */
384 PointerPde = MiAddressToPde(MmSessionSpace);
385 if (!PointerPde->u.Hard.Valid)
386 {
387 /* This means there's no valid session, bail out */
388 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
389 Address);
390 DbgBreakPoint();
391 return STATUS_ACCESS_VIOLATION;
392 }
393
394 /* Now get the PDE for the address itself */
395 PointerPde = MiAddressToPde(Address);
396 if (!PointerPde->u.Hard.Valid)
397 {
398 /* Do the swap, we should be good to go */
399 Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22;
400 PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
401 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
402
403 /* We had not allocated a page table for this session address yet, fail! */
404 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
405 PointerPde->u.Long, Address);
406 DbgBreakPoint();
407 return STATUS_ACCESS_VIOLATION;
408 }
409
410 /* It's valid, so there's nothing to do */
411 return STATUS_SUCCESS;
412 }
413
414 NTSTATUS
415 FASTCALL
416 MiCheckPdeForPagedPool(IN PVOID Address)
417 {
418 PMMPDE PointerPde;
419 NTSTATUS Status = STATUS_SUCCESS;
420
421 /* Check session PDE */
422 if (MI_IS_SESSION_ADDRESS(Address)) return MiCheckPdeForSessionSpace(Address);
423 if (MI_IS_SESSION_PTE(Address)) return MiCheckPdeForSessionSpace(Address);
424
425 //
426 // Check if this is a fault while trying to access the page table itself
427 //
428 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
429 {
430 //
431 // Send a hint to the page fault handler that this is only a valid fault
432 // if we already detected this was access within the page table range
433 //
434 PointerPde = (PMMPDE)MiAddressToPte(Address);
435 Status = STATUS_WAIT_1;
436 }
437 else if (Address < MmSystemRangeStart)
438 {
439 //
440 // This is totally illegal
441 //
442 return STATUS_ACCESS_VIOLATION;
443 }
444 else
445 {
446 //
447 // Get the PDE for the address
448 //
449 PointerPde = MiAddressToPde(Address);
450 }
451
452 //
453 // Check if it's not valid
454 //
455 if (PointerPde->u.Hard.Valid == 0)
456 {
457 //
458 // Copy it from our double-mapped system page directory
459 //
460 InterlockedExchangePte(PointerPde,
461 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
462 }
463
464 //
465 // Return status
466 //
467 return Status;
468 }
469 #else
470 NTSTATUS
471 FASTCALL
472 MiCheckPdeForPagedPool(IN PVOID Address)
473 {
474 return STATUS_ACCESS_VIOLATION;
475 }
476 #endif
477
478 VOID
479 NTAPI
480 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
481 {
482 PMMPTE ZeroPte;
483 MMPTE TempPte;
484 PMMPFN Pfn1;
485 PVOID ZeroAddress;
486
487 /* Get the PFN for this page */
488 Pfn1 = MiGetPfnEntry(PageFrameNumber);
489 ASSERT(Pfn1);
490
491 /* Grab a system PTE we can use to zero the page */
492 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
493 ASSERT(ZeroPte);
494
495 /* Initialize the PTE for it */
496 TempPte = ValidKernelPte;
497 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
498
499 /* Setup caching */
500 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
501 {
502 /* Write combining, no caching */
503 MI_PAGE_DISABLE_CACHE(&TempPte);
504 MI_PAGE_WRITE_COMBINED(&TempPte);
505 }
506 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
507 {
508 /* Write through, no caching */
509 MI_PAGE_DISABLE_CACHE(&TempPte);
510 MI_PAGE_WRITE_THROUGH(&TempPte);
511 }
512
513 /* Make the system PTE valid with our PFN */
514 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
515
516 /* Get the address it maps to, and zero it out */
517 ZeroAddress = MiPteToAddress(ZeroPte);
518 KeZeroPages(ZeroAddress, PAGE_SIZE);
519
520 /* Now get rid of it */
521 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
522 }
523
524 NTSTATUS
525 NTAPI
526 MiResolveDemandZeroFault(IN PVOID Address,
527 IN PMMPTE PointerPte,
528 IN PEPROCESS Process,
529 IN KIRQL OldIrql)
530 {
531 PFN_NUMBER PageFrameNumber = 0;
532 MMPTE TempPte;
533 BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
534 ULONG Color;
535 PMMPFN Pfn1;
536 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
537 Address,
538 Process);
539
540 /* Must currently only be called by paging path */
541 if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL))
542 {
543 /* Sanity check */
544 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
545
546 /* No forking yet */
547 ASSERT(Process->ForkInProgress == NULL);
548
549 /* Get process color */
550 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
551 ASSERT(Color != 0xFFFFFFFF);
552
553 /* We'll need a zero page */
554 NeedZero = TRUE;
555 }
556 else
557 {
558 /* Check if we need a zero page */
559 NeedZero = (OldIrql != MM_NOIRQL);
560
561 /* Session-backed image views must be zeroed */
562 if ((Process == HYDRA_PROCESS) &&
563 ((MI_IS_SESSION_IMAGE_ADDRESS(Address)) ||
564 ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs))))
565 {
566 NeedZero = TRUE;
567 }
568
569 /* Hardcode unknown color */
570 Color = 0xFFFFFFFF;
571 }
572
573 /* Check if the PFN database should be acquired */
574 if (OldIrql == MM_NOIRQL)
575 {
576 /* Acquire it and remember we should release it after */
577 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
578 HaveLock = TRUE;
579 }
580
581 /* We either manually locked the PFN DB, or already came with it locked */
582 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
583 ASSERT(PointerPte->u.Hard.Valid == 0);
584
585 /* Assert we have enough pages */
586 ASSERT(MmAvailablePages >= 32);
587
588 #if MI_TRACE_PFNS
589 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
590 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
591 #endif
592 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
593 if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
594
595 /* Do we need a zero page? */
596 if (Color != 0xFFFFFFFF)
597 {
598 /* Try to get one, if we couldn't grab a free page and zero it */
599 PageFrameNumber = MiRemoveZeroPageSafe(Color);
600 if (!PageFrameNumber)
601 {
602 /* We'll need a free page and zero it manually */
603 PageFrameNumber = MiRemoveAnyPage(Color);
604 NeedZero = TRUE;
605 }
606 }
607 else
608 {
609 /* Get a color, and see if we should grab a zero or non-zero page */
610 Color = MI_GET_NEXT_COLOR();
611 if (!NeedZero)
612 {
613 /* Process or system doesn't want a zero page, grab anything */
614 PageFrameNumber = MiRemoveAnyPage(Color);
615 }
616 else
617 {
618 /* System wants a zero page, obtain one */
619 PageFrameNumber = MiRemoveZeroPage(Color);
620 }
621 }
622
623 /* Initialize it */
624 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
625
626 /* Do we have the lock? */
627 if (HaveLock)
628 {
629 /* Release it */
630 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
631
632 /* Update performance counters */
633 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
634 }
635
636 /* Increment demand zero faults */
637 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
638
639 /* Zero the page if need be */
640 if (NeedZero) MiZeroPfn(PageFrameNumber);
641
642 /* Fault on user PDE, or fault on user PTE? */
643 if (PointerPte <= MiHighestUserPte)
644 {
645 /* User fault, build a user PTE */
646 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
647 PointerPte,
648 PointerPte->u.Soft.Protection,
649 PageFrameNumber);
650 }
651 else
652 {
653 /* This is a user-mode PDE, create a kernel PTE for it */
654 MI_MAKE_HARDWARE_PTE(&TempPte,
655 PointerPte,
656 PointerPte->u.Soft.Protection,
657 PageFrameNumber);
658 }
659
660 /* Set it dirty if it's a writable page */
661 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
662
663 /* Write it */
664 MI_WRITE_VALID_PTE(PointerPte, TempPte);
665
666 /* Did we manually acquire the lock */
667 if (HaveLock)
668 {
669 /* Get the PFN entry */
670 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
671
672 /* Windows does these sanity checks */
673 ASSERT(Pfn1->u1.Event == 0);
674 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
675 }
676
677 //
678 // It's all good now
679 //
680 DPRINT("Demand zero page has now been paged in\n");
681 return STATUS_PAGE_FAULT_DEMAND_ZERO;
682 }
683
684 NTSTATUS
685 NTAPI
686 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
687 IN PVOID Address,
688 IN PMMPTE PointerPte,
689 IN PMMPTE PointerProtoPte,
690 IN KIRQL OldIrql,
691 IN PMMPFN* LockedProtoPfn)
692 {
693 MMPTE TempPte;
694 PMMPTE OriginalPte, PageTablePte;
695 ULONG_PTR Protection;
696 PFN_NUMBER PageFrameIndex;
697 PMMPFN Pfn1, Pfn2;
698 BOOLEAN OriginalProtection, DirtyPage;
699
700 /* Must be called with an valid prototype PTE, with the PFN lock held */
701 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
702 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
703
704 /* Get the page */
705 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
706
707 /* Get the PFN entry and set it as a prototype PTE */
708 Pfn1 = MiGetPfnEntry(PageFrameIndex);
709 Pfn1->u3.e1.PrototypePte = 1;
710
711 /* Increment the share count for the page table */
712 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
713 // This could be beause MiDeletePte is now being called from strange code in Rosmm
714 PageTablePte = MiAddressToPte(PointerPte);
715 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
716 //Pfn2->u2.ShareCount++;
717 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
718
719 /* Check where we should be getting the protection information from */
720 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
721 {
722 /* Get the protection from the PTE, there's no real Proto PTE data */
723 Protection = PointerPte->u.Soft.Protection;
724
725 /* Remember that we did not use the proto protection */
726 OriginalProtection = FALSE;
727 }
728 else
729 {
730 /* Get the protection from the original PTE link */
731 OriginalPte = &Pfn1->OriginalPte;
732 Protection = OriginalPte->u.Soft.Protection;
733
734 /* Remember that we used the original protection */
735 OriginalProtection = TRUE;
736
737 /* Check if this was a write on a read only proto */
738 if ((StoreInstruction) && !(Protection & MM_READWRITE))
739 {
740 /* Clear the flag */
741 StoreInstruction = 0;
742 }
743 }
744
745 /* Check if this was a write on a non-COW page */
746 DirtyPage = FALSE;
747 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
748 {
749 /* Then the page should be marked dirty */
750 DirtyPage = TRUE;
751
752 /* ReactOS check */
753 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
754 }
755
756 /* Did we get a locked incoming PFN? */
757 if (*LockedProtoPfn)
758 {
759 /* Drop a reference */
760 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
761 MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
762 *LockedProtoPfn = NULL;
763 }
764
765 /* Release the PFN lock */
766 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
767
768 /* Remove caching bits */
769 Protection &= ~(MM_NOCACHE | MM_NOACCESS);
770
771 /* Setup caching */
772 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
773 {
774 /* Write combining, no caching */
775 MI_PAGE_DISABLE_CACHE(&TempPte);
776 MI_PAGE_WRITE_COMBINED(&TempPte);
777 }
778 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
779 {
780 /* Write through, no caching */
781 MI_PAGE_DISABLE_CACHE(&TempPte);
782 MI_PAGE_WRITE_THROUGH(&TempPte);
783 }
784
785 /* Check if this is a kernel or user address */
786 if (Address < MmSystemRangeStart)
787 {
788 /* Build the user PTE */
789 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
790 }
791 else
792 {
793 /* Build the kernel PTE */
794 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
795 }
796
797 /* Set the dirty flag if needed */
798 if (DirtyPage) TempPte.u.Hard.Dirty = TRUE;
799
800 /* Write the PTE */
801 MI_WRITE_VALID_PTE(PointerPte, TempPte);
802
803 /* Reset the protection if needed */
804 if (OriginalProtection) Protection = MM_ZERO_ACCESS;
805
806 /* Return success */
807 ASSERT(PointerPte == MiAddressToPte(Address));
808 return STATUS_SUCCESS;
809 }
810
811 NTSTATUS
812 NTAPI
813 MiResolveTransitionFault(IN PVOID FaultingAddress,
814 IN PMMPTE PointerPte,
815 IN PEPROCESS CurrentProcess,
816 IN KIRQL OldIrql,
817 OUT PVOID *InPageBlock)
818 {
819 PFN_NUMBER PageFrameIndex;
820 PMMPFN Pfn1;
821 MMPTE TempPte;
822 PMMPTE PointerToPteForProtoPage;
823 DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
824
825 /* Windowss does this check */
826 ASSERT(*InPageBlock == NULL);
827
828 /* ARM3 doesn't support this path */
829 ASSERT(OldIrql != MM_NOIRQL);
830
831 /* Capture the PTE and make sure it's in transition format */
832 TempPte = *PointerPte;
833 ASSERT((TempPte.u.Soft.Valid == 0) &&
834 (TempPte.u.Soft.Prototype == 0) &&
835 (TempPte.u.Soft.Transition == 1));
836
837 /* Get the PFN and the PFN entry */
838 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
839 DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
840 Pfn1 = MiGetPfnEntry(PageFrameIndex);
841
842 /* One more transition fault! */
843 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
844
845 /* This is from ARM3 -- Windows normally handles this here */
846 ASSERT(Pfn1->u4.InPageError == 0);
847
848 /* Not supported in ARM3 */
849 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
850
851 /* Windows checks there's some free pages and this isn't an in-page error */
852 ASSERT(MmAvailablePages > 0);
853 ASSERT(Pfn1->u4.InPageError == 0);
854
855 /* ReactOS checks for this */
856 ASSERT(MmAvailablePages > 32);
857
858 /* Was this a transition page in the valid list, or free/zero list? */
859 if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
860 {
861 /* All Windows does here is a bunch of sanity checks */
862 DPRINT1("Transition in active list\n");
863 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
864 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
865 ASSERT(Pfn1->u2.ShareCount != 0);
866 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
867 }
868 else
869 {
870 /* Otherwise, the page is removed from its list */
871 DPRINT1("Transition page in free/zero list\n");
872 MiUnlinkPageFromList(Pfn1);
873 MiReferenceUnusedPageAndBumpLockCount(Pfn1);
874 }
875
876 /* At this point, there should no longer be any in-page errors */
877 ASSERT(Pfn1->u4.InPageError == 0);
878
879 /* Check if this was a PFN with no more share references */
880 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
881
882 /* Bump the share count and make the page valid */
883 Pfn1->u2.ShareCount++;
884 Pfn1->u3.e1.PageLocation = ActiveAndValid;
885
886 /* Prototype PTEs are in paged pool, which itself might be in transition */
887 if (FaultingAddress >= MmSystemRangeStart)
888 {
889 /* Check if this is a paged pool PTE in transition state */
890 PointerToPteForProtoPage = MiAddressToPte(PointerPte);
891 TempPte = *PointerToPteForProtoPage;
892 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
893 {
894 /* This isn't yet supported */
895 DPRINT1("Double transition fault not yet supported\n");
896 ASSERT(FALSE);
897 }
898 }
899
900 /* Build the transition PTE -- maybe a macro? */
901 ASSERT(PointerPte->u.Hard.Valid == 0);
902 ASSERT(PointerPte->u.Trans.Prototype == 0);
903 ASSERT(PointerPte->u.Trans.Transition == 1);
904 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
905 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
906 MiDetermineUserGlobalPteMask(PointerPte);
907
908 /* Is the PTE writeable? */
909 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
910 (TempPte.u.Hard.CopyOnWrite == 0))
911 {
912 /* Make it dirty */
913 TempPte.u.Hard.Dirty = TRUE;
914 }
915 else
916 {
917 /* Make it clean */
918 TempPte.u.Hard.Dirty = FALSE;
919 }
920
921 /* Write the valid PTE */
922 MI_WRITE_VALID_PTE(PointerPte, TempPte);
923
924 /* Return success */
925 return STATUS_PAGE_FAULT_TRANSITION;
926 }
927
928 NTSTATUS
929 NTAPI
930 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
931 IN PVOID Address,
932 IN PMMPTE PointerPte,
933 IN PMMPTE PointerProtoPte,
934 IN OUT PMMPFN *OutPfn,
935 OUT PVOID *PageFileData,
936 OUT PMMPTE PteValue,
937 IN PEPROCESS Process,
938 IN KIRQL OldIrql,
939 IN PVOID TrapInformation)
940 {
941 MMPTE TempPte, PteContents;
942 PMMPFN Pfn1;
943 PFN_NUMBER PageFrameIndex;
944 NTSTATUS Status;
945 PVOID InPageBlock = NULL;
946
947 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
948 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
949 ASSERT(PointerPte->u.Hard.Valid == 0);
950 ASSERT(PointerPte->u.Soft.Prototype == 1);
951
952 /* Read the prototype PTE and check if it's valid */
953 TempPte = *PointerProtoPte;
954 if (TempPte.u.Hard.Valid == 1)
955 {
956 /* One more user of this mapped page */
957 PageFrameIndex = PFN_FROM_PTE(&TempPte);
958 Pfn1 = MiGetPfnEntry(PageFrameIndex);
959 Pfn1->u2.ShareCount++;
960
961 /* Call it a transition */
962 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
963
964 /* Complete the prototype PTE fault -- this will release the PFN lock */
965 return MiCompleteProtoPteFault(StoreInstruction,
966 Address,
967 PointerPte,
968 PointerProtoPte,
969 OldIrql,
970 OutPfn);
971 }
972
973 /* Make sure there's some protection mask */
974 if (TempPte.u.Long == 0)
975 {
976 /* Release the lock */
977 DPRINT1("Access on reserved section?\n");
978 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
979 return STATUS_ACCESS_VIOLATION;
980 }
981
982 /* There is no such thing as a decommitted prototype PTE */
983 NT_ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
984
985 /* Check for access rights on the PTE proper */
986 PteContents = *PointerPte;
987 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
988 {
989 if (!PteContents.u.Proto.ReadOnly)
990 {
991 /* Check for page acess in software */
992 Status = MiAccessCheck(PointerProtoPte,
993 StoreInstruction,
994 KernelMode,
995 TempPte.u.Soft.Protection,
996 TrapInformation,
997 TRUE);
998 ASSERT(Status == STATUS_SUCCESS);
999
1000 /* Check for copy on write page */
1001 if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
1002 {
1003 /* Not yet supported */
1004 ASSERT(FALSE);
1005 }
1006 }
1007 }
1008 else
1009 {
1010 /* Check for copy on write page */
1011 if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
1012 {
1013 /* Not yet supported */
1014 ASSERT(FALSE);
1015 }
1016 }
1017
1018 /* Check for clone PTEs */
1019 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
1020
1021 /* We don't support mapped files yet */
1022 ASSERT(TempPte.u.Soft.Prototype == 0);
1023
1024 /* We might however have transition PTEs */
1025 if (TempPte.u.Soft.Transition == 1)
1026 {
1027 /* Resolve the transition fault */
1028 ASSERT(OldIrql != MM_NOIRQL);
1029 Status = MiResolveTransitionFault(Address,
1030 PointerProtoPte,
1031 Process,
1032 OldIrql,
1033 &InPageBlock);
1034 ASSERT(NT_SUCCESS(Status));
1035 }
1036 else
1037 {
1038 /* We also don't support paged out pages */
1039 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1040
1041 /* Resolve the demand zero fault */
1042 Status = MiResolveDemandZeroFault(Address,
1043 PointerProtoPte,
1044 Process,
1045 OldIrql);
1046 ASSERT(NT_SUCCESS(Status));
1047 }
1048
1049 /* Complete the prototype PTE fault -- this will release the PFN lock */
1050 ASSERT(PointerPte->u.Hard.Valid == 0);
1051 return MiCompleteProtoPteFault(StoreInstruction,
1052 Address,
1053 PointerPte,
1054 PointerProtoPte,
1055 OldIrql,
1056 OutPfn);
1057 }
1058
1059 NTSTATUS
1060 NTAPI
1061 MiDispatchFault(IN BOOLEAN StoreInstruction,
1062 IN PVOID Address,
1063 IN PMMPTE PointerPte,
1064 IN PMMPTE PointerProtoPte,
1065 IN BOOLEAN Recursive,
1066 IN PEPROCESS Process,
1067 IN PVOID TrapInformation,
1068 IN PMMVAD Vad)
1069 {
1070 MMPTE TempPte;
1071 KIRQL OldIrql, LockIrql;
1072 NTSTATUS Status;
1073 PMMPTE SuperProtoPte;
1074 PMMPFN Pfn1, OutPfn = NULL;
1075 PFN_NUMBER PageFrameIndex;
1076 PFN_COUNT PteCount, ProcessedPtes;
1077 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1078 Address,
1079 Process);
1080
1081 /* Make sure the addresses are ok */
1082 ASSERT(PointerPte == MiAddressToPte(Address));
1083
1084 //
1085 // Make sure APCs are off and we're not at dispatch
1086 //
1087 OldIrql = KeGetCurrentIrql();
1088 ASSERT(OldIrql <= APC_LEVEL);
1089 ASSERT(KeAreAllApcsDisabled() == TRUE);
1090
1091 //
1092 // Grab a copy of the PTE
1093 //
1094 TempPte = *PointerPte;
1095
1096 /* Do we have a prototype PTE? */
1097 if (PointerProtoPte)
1098 {
1099 /* This should never happen */
1100 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
1101
1102 /* Check if this is a kernel-mode address */
1103 SuperProtoPte = MiAddressToPte(PointerProtoPte);
1104 if (Address >= MmSystemRangeStart)
1105 {
1106 /* Lock the PFN database */
1107 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1108
1109 /* Has the PTE been made valid yet? */
1110 if (!SuperProtoPte->u.Hard.Valid)
1111 {
1112 ASSERT(FALSE);
1113 }
1114 else if (PointerPte->u.Hard.Valid == 1)
1115 {
1116 ASSERT(FALSE);
1117 }
1118
1119 /* Resolve the fault -- this will release the PFN lock */
1120 Status = MiResolveProtoPteFault(StoreInstruction,
1121 Address,
1122 PointerPte,
1123 PointerProtoPte,
1124 &OutPfn,
1125 NULL,
1126 NULL,
1127 Process,
1128 LockIrql,
1129 TrapInformation);
1130 ASSERT(Status == STATUS_SUCCESS);
1131
1132 /* Complete this as a transition fault */
1133 ASSERT(OldIrql == KeGetCurrentIrql());
1134 ASSERT(OldIrql <= APC_LEVEL);
1135 ASSERT(KeAreAllApcsDisabled() == TRUE);
1136 return Status;
1137 }
1138 else
1139 {
1140 /* We only handle the lookup path */
1141 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
1142
1143 /* Is there a non-image VAD? */
1144 if ((Vad) &&
1145 (Vad->u.VadFlags.VadType != VadImageMap) &&
1146 !(Vad->u2.VadFlags2.ExtendableFile))
1147 {
1148 /* One day, ReactOS will cluster faults */
1149 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
1150 DPRINT("Should cluster fault, but won't\n");
1151 }
1152
1153 /* Only one PTE to handle for now */
1154 PteCount = 1;
1155 ProcessedPtes = 0;
1156
1157 /* Lock the PFN database */
1158 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1159
1160 /* We only handle the valid path */
1161 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
1162
1163 /* Capture the PTE */
1164 TempPte = *PointerProtoPte;
1165
1166 /* Loop to handle future case of clustered faults */
1167 while (TRUE)
1168 {
1169 /* For our current usage, this should be true */
1170 if (TempPte.u.Hard.Valid == 1)
1171 {
1172 /* Bump the share count on the PTE */
1173 PageFrameIndex = PFN_FROM_PTE(&TempPte);
1174 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1175 Pfn1->u2.ShareCount++;
1176 }
1177 else if ((TempPte.u.Soft.Prototype == 0) &&
1178 (TempPte.u.Soft.Transition == 1))
1179 {
1180 /* This is a standby page, bring it back from the cache */
1181 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1182 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
1183 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1184 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1185
1186 /* Should not yet happen in ReactOS */
1187 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
1188 ASSERT(Pfn1->u4.InPageError == 0);
1189
1190 /* Get the page */
1191 MiUnlinkPageFromList(Pfn1);
1192
1193 /* Bump its reference count */
1194 ASSERT(Pfn1->u2.ShareCount == 0);
1195 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1196 Pfn1->u2.ShareCount++;
1197
1198 /* Make it valid again */
1199 /* This looks like another macro.... */
1200 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1201 ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1202 ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1203 ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1204 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1205 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1206 TempPte.u.Hard.Valid = 1;
1207 TempPte.u.Hard.Accessed = 1;
1208
1209 /* Is the PTE writeable? */
1210 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
1211 (TempPte.u.Hard.CopyOnWrite == 0))
1212 {
1213 /* Make it dirty */
1214 TempPte.u.Hard.Dirty = TRUE;
1215 }
1216 else
1217 {
1218 /* Make it clean */
1219 TempPte.u.Hard.Dirty = FALSE;
1220 }
1221
1222 /* Write the valid PTE */
1223 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1224 ASSERT(PointerPte->u.Hard.Valid == 0);
1225 }
1226 else
1227 {
1228 /* Page is invalid, get out of the loop */
1229 break;
1230 }
1231
1232 /* One more done, was it the last? */
1233 if (++ProcessedPtes == PteCount)
1234 {
1235 /* Complete the fault */
1236 MiCompleteProtoPteFault(StoreInstruction,
1237 Address,
1238 PointerPte,
1239 PointerProtoPte,
1240 LockIrql,
1241 &OutPfn);
1242
1243 /* THIS RELEASES THE PFN LOCK! */
1244 break;
1245 }
1246
1247 /* No clustered faults yet */
1248 ASSERT(FALSE);
1249 }
1250
1251 /* Did we resolve the fault? */
1252 if (ProcessedPtes)
1253 {
1254 /* Bump the transition count */
1255 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1256 ProcessedPtes--;
1257
1258 /* Loop all the processing we did */
1259 ASSERT(ProcessedPtes == 0);
1260
1261 /* Complete this as a transition fault */
1262 ASSERT(OldIrql == KeGetCurrentIrql());
1263 ASSERT(OldIrql <= APC_LEVEL);
1264 ASSERT(KeAreAllApcsDisabled() == TRUE);
1265 return STATUS_PAGE_FAULT_TRANSITION;
1266 }
1267
1268 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1269 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1270 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1271 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1272 ASSERT(PointerPte->u.Hard.Valid == 0);
1273
1274 /* Resolve the fault -- this will release the PFN lock */
1275 Status = MiResolveProtoPteFault(StoreInstruction,
1276 Address,
1277 PointerPte,
1278 PointerProtoPte,
1279 &OutPfn,
1280 NULL,
1281 NULL,
1282 Process,
1283 LockIrql,
1284 TrapInformation);
1285 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1286 //ASSERT(Status != STATUS_REFAULT);
1287 //ASSERT(Status != STATUS_PTE_CHANGED);
1288
1289 /* Did the routine clean out the PFN or should we? */
1290 if (OutPfn)
1291 {
1292 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1293 ASSERT(PointerProtoPte != NULL);
1294 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1295
1296 /* Dereference the locked PFN */
1297 MiDereferencePfnAndDropLockCount(OutPfn);
1298 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1299
1300 /* And now release the lock */
1301 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1302 }
1303
1304 /* Complete this as a transition fault */
1305 ASSERT(OldIrql == KeGetCurrentIrql());
1306 ASSERT(OldIrql <= APC_LEVEL);
1307 ASSERT(KeAreAllApcsDisabled() == TRUE);
1308 return Status;
1309 }
1310 }
1311
1312 //
1313 // The PTE must be invalid but not completely empty. It must also not be a
1314 // prototype PTE as that scenario should've been handled above. These are
1315 // all Windows checks
1316 //
1317 ASSERT(TempPte.u.Hard.Valid == 0);
1318 ASSERT(TempPte.u.Soft.Prototype == 0);
1319 ASSERT(TempPte.u.Long != 0);
1320
1321 //
1322 // No transition or page file software PTEs in ARM3 yet, so this must be a
1323 // demand zero page. These are all ReactOS checks
1324 //
1325 ASSERT(TempPte.u.Soft.Transition == 0);
1326 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1327
1328 //
1329 // If we got this far, the PTE can only be a demand zero PTE, which is what
1330 // we want. Go handle it!
1331 //
1332 Status = MiResolveDemandZeroFault(Address,
1333 PointerPte,
1334 Process,
1335 MM_NOIRQL);
1336 ASSERT(KeAreAllApcsDisabled() == TRUE);
1337 if (NT_SUCCESS(Status))
1338 {
1339 //
1340 // Make sure we're returning in a sane state and pass the status down
1341 //
1342 ASSERT(OldIrql == KeGetCurrentIrql());
1343 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1344 return Status;
1345 }
1346
1347 //
1348 // Generate an access fault
1349 //
1350 return STATUS_ACCESS_VIOLATION;
1351 }
1352
1353 NTSTATUS
1354 NTAPI
1355 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1356 IN PVOID Address,
1357 IN KPROCESSOR_MODE Mode,
1358 IN PVOID TrapInformation)
1359 {
1360 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1361 PMMPTE ProtoPte = NULL;
1362 PMMPTE PointerPte = MiAddressToPte(Address);
1363 PMMPDE PointerPde = MiAddressToPde(Address);
1364 #if (_MI_PAGING_LEVELS >= 3)
1365 PMMPDE PointerPpe = MiAddressToPpe(Address);
1366 #if (_MI_PAGING_LEVELS == 4)
1367 PMMPDE PointerPxe = MiAddressToPxe(Address);
1368 #endif
1369 #endif
1370 MMPTE TempPte;
1371 PETHREAD CurrentThread;
1372 PEPROCESS CurrentProcess;
1373 NTSTATUS Status;
1374 PMMSUPPORT WorkingSet;
1375 ULONG ProtectionCode;
1376 PMMVAD Vad;
1377 PFN_NUMBER PageFrameIndex;
1378 ULONG Color;
1379 BOOLEAN IsSessionAddress;
1380 PMMPFN Pfn1;
1381 DPRINT("ARM3 FAULT AT: %p\n", Address);
1382
1383 /* Check for page fault on high IRQL */
1384 if (OldIrql > APC_LEVEL)
1385 {
1386 #if (_MI_PAGING_LEVELS < 3)
1387 /* Could be a page table for paged pool, which we'll allow */
1388 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1389 MiCheckPdeForPagedPool(Address);
1390 #endif
1391 /* Check if any of the top-level pages are invalid */
1392 if (
1393 #if (_MI_PAGING_LEVELS == 4)
1394 (PointerPxe->u.Hard.Valid == 0) ||
1395 #endif
1396 #if (_MI_PAGING_LEVELS >= 3)
1397 (PointerPpe->u.Hard.Valid == 0) ||
1398 #endif
1399 (PointerPde->u.Hard.Valid == 0) ||
1400 (PointerPte->u.Hard.Valid == 0))
1401 {
1402 /* This fault is not valid, print out some debugging help */
1403 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1404 Address,
1405 OldIrql);
1406 if (TrapInformation)
1407 {
1408 PKTRAP_FRAME TrapFrame = TrapInformation;
1409 #ifdef _M_IX86
1410 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1411 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1412 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1413 #elif defined(_M_AMD64)
1414 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1415 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1416 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1417 #endif
1418 }
1419
1420 /* Tell the trap handler to fail */
1421 return STATUS_IN_PAGE_ERROR | 0x10000000;
1422 }
1423
1424 /* Not yet implemented in ReactOS */
1425 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1426 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1427
1428 /* Check if this was a write */
1429 if (StoreInstruction)
1430 {
1431 /* Was it to a read-only page? */
1432 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1433 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1434 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1435 {
1436 /* Crash with distinguished bugcheck code */
1437 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1438 (ULONG_PTR)Address,
1439 PointerPte->u.Long,
1440 (ULONG_PTR)TrapInformation,
1441 10);
1442 }
1443 }
1444
1445 /* Nothing is actually wrong */
1446 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
1447 return STATUS_SUCCESS;
1448 }
1449
1450 /* Check for kernel fault address */
1451 if (Address >= MmSystemRangeStart)
1452 {
1453 /* Bail out, if the fault came from user mode */
1454 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1455
1456 #if (_MI_PAGING_LEVELS == 4)
1457 /* AMD64 system, check if PXE is invalid */
1458 if (PointerPxe->u.Hard.Valid == 0)
1459 {
1460 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1461 (ULONG_PTR)Address,
1462 StoreInstruction,
1463 (ULONG_PTR)TrapInformation,
1464 7);
1465 }
1466 #endif
1467 #if (_MI_PAGING_LEVELS == 4)
1468 /* PAE/AMD64 system, check if PPE is invalid */
1469 if (PointerPpe->u.Hard.Valid == 0)
1470 {
1471 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1472 (ULONG_PTR)Address,
1473 StoreInstruction,
1474 (ULONG_PTR)TrapInformation,
1475 5);
1476 }
1477 #endif
1478 #if (_MI_PAGING_LEVELS == 2)
1479 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1480 MiCheckPdeForPagedPool(Address);
1481 #endif
1482
1483 /* Check if the PDE is invalid */
1484 if (PointerPde->u.Hard.Valid == 0)
1485 {
1486 /* PDE (still) not valid, kill the system */
1487 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1488 (ULONG_PTR)Address,
1489 StoreInstruction,
1490 (ULONG_PTR)TrapInformation,
1491 2);
1492 }
1493
1494 /* Not handling session faults yet */
1495 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1496
1497 /* The PDE is valid, so read the PTE */
1498 TempPte = *PointerPte;
1499 if (TempPte.u.Hard.Valid == 1)
1500 {
1501 /* Check if this was system space or session space */
1502 if (!IsSessionAddress)
1503 {
1504 /* Check if the PTE is still valid under PFN lock */
1505 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1506 TempPte = *PointerPte;
1507 if (TempPte.u.Hard.Valid)
1508 {
1509 /* Check if this was a write */
1510 if (StoreInstruction)
1511 {
1512 /* Was it to a read-only page? */
1513 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1514 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1515 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1516 {
1517 /* Crash with distinguished bugcheck code */
1518 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1519 (ULONG_PTR)Address,
1520 PointerPte->u.Long,
1521 (ULONG_PTR)TrapInformation,
1522 11);
1523 }
1524 }
1525 }
1526
1527 /* Release PFN lock and return all good */
1528 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1529 return STATUS_SUCCESS;
1530 }
1531 }
1532 #if (_MI_PAGING_LEVELS == 2)
1533 /* Check if this was a session PTE that needs to remap the session PDE */
1534 if (MI_IS_SESSION_PTE(Address))
1535 {
1536 /* Do the remapping */
1537 Status = MiCheckPdeForSessionSpace(Address);
1538 if (!NT_SUCCESS(Status))
1539 {
1540 /* It failed, this address is invalid */
1541 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1542 (ULONG_PTR)Address,
1543 StoreInstruction,
1544 (ULONG_PTR)TrapInformation,
1545 6);
1546 }
1547 }
1548 #else
1549
1550 _WARN("Session space stuff is not implemented yet!")
1551
1552 #endif
1553
1554 /* Check for a fault on the page table or hyperspace */
1555 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1556 {
1557 #if (_MI_PAGING_LEVELS < 3)
1558 /* Windows does this check but I don't understand why -- it's done above! */
1559 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1560 #endif
1561 /* Handle this as a user mode fault */
1562 goto UserFault;
1563 }
1564
1565 /* Get the current thread */
1566 CurrentThread = PsGetCurrentThread();
1567
1568 /* What kind of address is this */
1569 if (!IsSessionAddress)
1570 {
1571 /* Use the system working set */
1572 WorkingSet = &MmSystemCacheWs;
1573 CurrentProcess = NULL;
1574
1575 /* Make sure we don't have a recursive working set lock */
1576 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1577 (CurrentThread->OwnsProcessWorkingSetShared) ||
1578 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1579 (CurrentThread->OwnsSystemWorkingSetShared) ||
1580 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1581 (CurrentThread->OwnsSessionWorkingSetShared))
1582 {
1583 /* Fail */
1584 return STATUS_IN_PAGE_ERROR | 0x10000000;
1585 }
1586 }
1587 else
1588 {
1589 /* Use the session process and working set */
1590 CurrentProcess = HYDRA_PROCESS;
1591 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1592
1593 /* Make sure we don't have a recursive working set lock */
1594 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1595 (CurrentThread->OwnsSessionWorkingSetShared))
1596 {
1597 /* Fail */
1598 return STATUS_IN_PAGE_ERROR | 0x10000000;
1599 }
1600 }
1601
1602 /* Acquire the working set lock */
1603 KeRaiseIrql(APC_LEVEL, &LockIrql);
1604 MiLockWorkingSet(CurrentThread, WorkingSet);
1605
1606 /* Re-read PTE now that we own the lock */
1607 TempPte = *PointerPte;
1608 if (TempPte.u.Hard.Valid == 1)
1609 {
1610 /* Check if this was a write */
1611 if (StoreInstruction)
1612 {
1613 /* Was it to a read-only page that is not copy on write? */
1614 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1615 if (!(TempPte.u.Long & PTE_READWRITE) &&
1616 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1617 !(TempPte.u.Hard.CopyOnWrite))
1618 {
1619 /* Case not yet handled */
1620 ASSERT(!IsSessionAddress);
1621
1622 /* Crash with distinguished bugcheck code */
1623 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1624 (ULONG_PTR)Address,
1625 TempPte.u.Long,
1626 (ULONG_PTR)TrapInformation,
1627 12);
1628 }
1629 }
1630
1631 /* Check for read-only write in session space */
1632 if ((IsSessionAddress) &&
1633 (StoreInstruction) &&
1634 !(TempPte.u.Hard.Write))
1635 {
1636 /* Sanity check */
1637 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1638
1639 /* Was this COW? */
1640 if (TempPte.u.Hard.CopyOnWrite == 0)
1641 {
1642 /* Then this is not allowed */
1643 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1644 (ULONG_PTR)Address,
1645 (ULONG_PTR)TempPte.u.Long,
1646 (ULONG_PTR)TrapInformation,
1647 13);
1648 }
1649
1650 /* Otherwise, handle COW */
1651 ASSERT(FALSE);
1652 }
1653
1654 /* Release the working set */
1655 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1656 KeLowerIrql(LockIrql);
1657
1658 /* Otherwise, the PDE was probably invalid, and all is good now */
1659 return STATUS_SUCCESS;
1660 }
1661
1662 /* Check one kind of prototype PTE */
1663 if (TempPte.u.Soft.Prototype)
1664 {
1665 /* Make sure protected pool is on, and that this is a pool address */
1666 if ((MmProtectFreedNonPagedPool) &&
1667 (((Address >= MmNonPagedPoolStart) &&
1668 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1669 MmSizeOfNonPagedPoolInBytes))) ||
1670 ((Address >= MmNonPagedPoolExpansionStart) &&
1671 (Address < MmNonPagedPoolEnd))))
1672 {
1673 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1674 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1675 (ULONG_PTR)Address,
1676 StoreInstruction,
1677 Mode,
1678 4);
1679 }
1680
1681 /* Get the prototype PTE! */
1682 ProtoPte = MiProtoPteToPte(&TempPte);
1683
1684 /* Do we need to locate the prototype PTE in session space? */
1685 if ((IsSessionAddress) &&
1686 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1687 {
1688 /* Yep, go find it as well as the VAD for it */
1689 ProtoPte = MiCheckVirtualAddress(Address,
1690 &ProtectionCode,
1691 &Vad);
1692 ASSERT(ProtoPte != NULL);
1693 }
1694 }
1695 else
1696 {
1697 /* We don't implement transition PTEs */
1698 ASSERT(TempPte.u.Soft.Transition == 0);
1699
1700 /* Check for no-access PTE */
1701 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1702 {
1703 /* Bugcheck the system! */
1704 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1705 (ULONG_PTR)Address,
1706 StoreInstruction,
1707 (ULONG_PTR)TrapInformation,
1708 1);
1709 }
1710 }
1711
1712 /* Check for demand page */
1713 if ((StoreInstruction) &&
1714 !(ProtoPte) &&
1715 !(IsSessionAddress) &&
1716 !(TempPte.u.Hard.Valid))
1717 {
1718 /* Get the protection code */
1719 ASSERT(TempPte.u.Soft.Transition == 0);
1720 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1721 {
1722 /* Bugcheck the system! */
1723 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1724 (ULONG_PTR)Address,
1725 TempPte.u.Long,
1726 (ULONG_PTR)TrapInformation,
1727 14);
1728 }
1729 }
1730
1731 /* Now do the real fault handling */
1732 Status = MiDispatchFault(StoreInstruction,
1733 Address,
1734 PointerPte,
1735 ProtoPte,
1736 FALSE,
1737 CurrentProcess,
1738 TrapInformation,
1739 NULL);
1740
1741 /* Release the working set */
1742 ASSERT(KeAreAllApcsDisabled() == TRUE);
1743 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1744 KeLowerIrql(LockIrql);
1745
1746 /* We are done! */
1747 DPRINT("Fault resolved with status: %lx\n", Status);
1748 return Status;
1749 }
1750
1751 /* This is a user fault */
1752 UserFault:
1753 CurrentThread = PsGetCurrentThread();
1754 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1755
1756 /* Lock the working set */
1757 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1758
1759 #if (_MI_PAGING_LEVELS == 4)
1760 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1761 // also this is missing the page count increment
1762 /* Check if the PXE is valid */
1763 if (PointerPxe->u.Hard.Valid == 0)
1764 {
1765 /* Right now, we only handle scenarios where the PXE is totally empty */
1766 ASSERT(PointerPxe->u.Long == 0);
1767 #if 0
1768 /* Resolve a demand zero fault */
1769 Status = MiResolveDemandZeroFault(PointerPpe,
1770 MM_READWRITE,
1771 CurrentProcess,
1772 MM_NOIRQL);
1773 #endif
1774 /* We should come back with a valid PXE */
1775 ASSERT(PointerPxe->u.Hard.Valid == 1);
1776 }
1777 #endif
1778
1779 #if (_MI_PAGING_LEVELS >= 3)
1780 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1781 // also this is missing the page count increment
1782 /* Check if the PPE is valid */
1783 if (PointerPpe->u.Hard.Valid == 0)
1784 {
1785 /* Right now, we only handle scenarios where the PPE is totally empty */
1786 ASSERT(PointerPpe->u.Long == 0);
1787 #if 0
1788 /* Resolve a demand zero fault */
1789 Status = MiResolveDemandZeroFault(PointerPde,
1790 MM_READWRITE,
1791 CurrentProcess,
1792 MM_NOIRQL);
1793 #endif
1794 /* We should come back with a valid PPE */
1795 ASSERT(PointerPpe->u.Hard.Valid == 1);
1796 }
1797 #endif
1798
1799 /* Check if the PDE is valid */
1800 if (PointerPde->u.Hard.Valid == 0)
1801 {
1802 /* Right now, we only handle scenarios where the PDE is totally empty */
1803 ASSERT(PointerPde->u.Long == 0);
1804
1805 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1806 #if MI_TRACE_PFNS
1807 UserPdeFault = TRUE;
1808 #endif
1809 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1810 if (ProtectionCode == MM_NOACCESS)
1811 {
1812 #if (_MI_PAGING_LEVELS == 2)
1813 /* Could be a page table for paged pool */
1814 MiCheckPdeForPagedPool(Address);
1815 #endif
1816 /* Has the code above changed anything -- is this now a valid PTE? */
1817 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1818
1819 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1820 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1821 return Status;
1822 }
1823
1824 /* Write a demand-zero PDE */
1825 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1826
1827 /* Dispatch the fault */
1828 Status = MiDispatchFault(TRUE,
1829 PointerPte,
1830 PointerPde,
1831 NULL,
1832 FALSE,
1833 PsGetCurrentProcess(),
1834 TrapInformation,
1835 NULL);
1836 #if MI_TRACE_PFNS
1837 UserPdeFault = FALSE;
1838 #endif
1839 /* We should come back with APCs enabled, and with a valid PDE */
1840 ASSERT(KeAreAllApcsDisabled() == TRUE);
1841 ASSERT(PointerPde->u.Hard.Valid == 1);
1842 }
1843 else
1844 {
1845 /* Not yet implemented in ReactOS */
1846 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1847 }
1848
1849 /* Now capture the PTE. Ignore virtual faults for now */
1850 TempPte = *PointerPte;
1851 ASSERT(TempPte.u.Hard.Valid == 0);
1852
1853 /* Quick check for demand-zero */
1854 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1855 {
1856 /* Resolve the fault */
1857 MiResolveDemandZeroFault(Address,
1858 PointerPte,
1859 CurrentProcess,
1860 MM_NOIRQL);
1861
1862 /* Return the status */
1863 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1864 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1865 }
1866
1867 /* Check for zero PTE */
1868 if (TempPte.u.Long == 0)
1869 {
1870 /* Check if this address range belongs to a valid allocation (VAD) */
1871 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1872 if (ProtectionCode == MM_NOACCESS)
1873 {
1874 #if (_MI_PAGING_LEVELS == 2)
1875 /* Could be a page table for paged pool */
1876 MiCheckPdeForPagedPool(Address);
1877 #endif
1878 /* Has the code above changed anything -- is this now a valid PTE? */
1879 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1880
1881 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1882 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1883 return Status;
1884 }
1885
1886 /*
1887 * Check if this is a real user-mode address or actually a kernel-mode
1888 * page table for a user mode address
1889 */
1890 if (Address <= MM_HIGHEST_USER_ADDRESS)
1891 {
1892 /* Add an additional page table reference */
1893 MiIncrementPageTableReferences(Address);
1894 }
1895
1896 /* Is this a guard page? */
1897 if (ProtectionCode & MM_GUARDPAGE)
1898 {
1899 /* The VAD protection cannot be MM_DECOMMIT! */
1900 NT_ASSERT(ProtectionCode != MM_DECOMMIT);
1901
1902 /* Remove the bit */
1903 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
1904 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1905
1906 /* Not supported */
1907 ASSERT(ProtoPte == NULL);
1908 ASSERT(CurrentThread->ApcNeeded == 0);
1909
1910 /* Drop the working set lock */
1911 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1912 ASSERT(KeGetCurrentIrql() == OldIrql);
1913
1914 /* Handle stack expansion */
1915 return MiCheckForUserStackOverflow(Address, TrapInformation);
1916 }
1917
1918 /* Did we get a prototype PTE back? */
1919 if (!ProtoPte)
1920 {
1921 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1922 if (PointerPde == MiAddressToPde(PTE_BASE))
1923 {
1924 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1925 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1926 }
1927 else
1928 {
1929 /* No, create a new PTE. First, write the protection */
1930 TempPte.u.Soft.Protection = ProtectionCode;
1931 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1932 }
1933
1934 /* Lock the PFN database since we're going to grab a page */
1935 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1936
1937 /* Make sure we have enough pages */
1938 ASSERT(MmAvailablePages >= 32);
1939
1940 /* Try to get a zero page */
1941 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1942 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1943 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1944 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1945 if (!PageFrameIndex)
1946 {
1947 /* Grab a page out of there. Later we should grab a colored zero page */
1948 PageFrameIndex = MiRemoveAnyPage(Color);
1949 ASSERT(PageFrameIndex);
1950
1951 /* Release the lock since we need to do some zeroing */
1952 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1953
1954 /* Zero out the page, since it's for user-mode */
1955 MiZeroPfn(PageFrameIndex);
1956
1957 /* Grab the lock again so we can initialize the PFN entry */
1958 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1959 }
1960
1961 /* Initialize the PFN entry now */
1962 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1963
1964 /* And we're done with the lock */
1965 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1966
1967 /* Increment the count of pages in the process */
1968 CurrentProcess->NumberOfPrivatePages++;
1969
1970 /* One more demand-zero fault */
1971 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1972
1973 /* Fault on user PDE, or fault on user PTE? */
1974 if (PointerPte <= MiHighestUserPte)
1975 {
1976 /* User fault, build a user PTE */
1977 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1978 PointerPte,
1979 PointerPte->u.Soft.Protection,
1980 PageFrameIndex);
1981 }
1982 else
1983 {
1984 /* This is a user-mode PDE, create a kernel PTE for it */
1985 MI_MAKE_HARDWARE_PTE(&TempPte,
1986 PointerPte,
1987 PointerPte->u.Soft.Protection,
1988 PageFrameIndex);
1989 }
1990
1991 /* Write the dirty bit for writeable pages */
1992 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1993
1994 /* And now write down the PTE, making the address valid */
1995 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1996 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1997 ASSERT(Pfn1->u1.Event == NULL);
1998
1999 /* Demand zero */
2000 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2001 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2002 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2003 }
2004
2005 /* We should have a valid protection here */
2006 ASSERT(ProtectionCode != 0x100);
2007
2008 /* Write the prototype PTE */
2009 TempPte = PrototypePte;
2010 TempPte.u.Soft.Protection = ProtectionCode;
2011 NT_ASSERT(TempPte.u.Long != 0);
2012 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2013 }
2014 else
2015 {
2016 /* Get the protection code and check if this is a proto PTE */
2017 ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2018 if (TempPte.u.Soft.Prototype)
2019 {
2020 /* Do we need to go find the real PTE? */
2021 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2022 {
2023 /* Get the prototype pte and VAD for it */
2024 ProtoPte = MiCheckVirtualAddress(Address,
2025 &ProtectionCode,
2026 &Vad);
2027 if (!ProtoPte)
2028 {
2029 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2030 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2031 return STATUS_ACCESS_VIOLATION;
2032 }
2033 }
2034 else
2035 {
2036 /* Get the prototype PTE! */
2037 ProtoPte = MiProtoPteToPte(&TempPte);
2038
2039 /* Is it read-only */
2040 if (TempPte.u.Proto.ReadOnly)
2041 {
2042 /* Set read-only code */
2043 ProtectionCode = MM_READONLY;
2044 }
2045 else
2046 {
2047 /* Set unknown protection */
2048 ProtectionCode = 0x100;
2049 ASSERT(CurrentProcess->CloneRoot != NULL);
2050 }
2051 }
2052 }
2053 }
2054
2055 /* Do we have a valid protection code? */
2056 if (ProtectionCode != 0x100)
2057 {
2058 /* Run a software access check first, including to detect guard pages */
2059 Status = MiAccessCheck(PointerPte,
2060 StoreInstruction,
2061 Mode,
2062 ProtectionCode,
2063 TrapInformation,
2064 FALSE);
2065 if (Status != STATUS_SUCCESS)
2066 {
2067 /* Not supported */
2068 ASSERT(CurrentThread->ApcNeeded == 0);
2069
2070 /* Drop the working set lock */
2071 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2072 ASSERT(KeGetCurrentIrql() == OldIrql);
2073
2074 /* Did we hit a guard page? */
2075 if (Status == STATUS_GUARD_PAGE_VIOLATION)
2076 {
2077 /* Handle stack expansion */
2078 return MiCheckForUserStackOverflow(Address, TrapInformation);
2079 }
2080
2081 /* Otherwise, fail back to the caller directly */
2082 return Status;
2083 }
2084 }
2085
2086 /* Dispatch the fault */
2087 Status = MiDispatchFault(StoreInstruction,
2088 Address,
2089 PointerPte,
2090 ProtoPte,
2091 FALSE,
2092 CurrentProcess,
2093 TrapInformation,
2094 Vad);
2095
2096 /* Return the status */
2097 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2098 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2099 return Status;
2100 }
2101
2102 NTSTATUS
2103 NTAPI
2104 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2105 {
2106 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2107 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2108
2109 *ExecuteOptions = 0;
2110
2111 if (CurrentProcess->Flags.ExecuteDisable)
2112 {
2113 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2114 }
2115
2116 if (CurrentProcess->Flags.ExecuteEnable)
2117 {
2118 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2119 }
2120
2121 if (CurrentProcess->Flags.DisableThunkEmulation)
2122 {
2123 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2124 }
2125
2126 if (CurrentProcess->Flags.Permanent)
2127 {
2128 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2129 }
2130
2131 if (CurrentProcess->Flags.ExecuteDispatchEnable)
2132 {
2133 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2134 }
2135
2136 if (CurrentProcess->Flags.ImageDispatchEnable)
2137 {
2138 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2139 }
2140
2141 return STATUS_SUCCESS;
2142 }
2143
2144 NTSTATUS
2145 NTAPI
2146 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2147 {
2148 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2149 KLOCK_QUEUE_HANDLE ProcessLock;
2150 NTSTATUS Status = STATUS_ACCESS_DENIED;
2151 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2152
2153 /* Only accept valid flags */
2154 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2155 {
2156 /* Fail */
2157 DPRINT1("Invalid no-execute options\n");
2158 return STATUS_INVALID_PARAMETER;
2159 }
2160
2161 /* Change the NX state in the process lock */
2162 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
2163
2164 /* Don't change anything if the permanent flag was set */
2165 if (!CurrentProcess->Flags.Permanent)
2166 {
2167 /* Start by assuming it's not disabled */
2168 CurrentProcess->Flags.ExecuteDisable = FALSE;
2169
2170 /* Now process each flag and turn the equivalent bit on */
2171 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2172 {
2173 CurrentProcess->Flags.ExecuteDisable = TRUE;
2174 }
2175 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2176 {
2177 CurrentProcess->Flags.ExecuteEnable = TRUE;
2178 }
2179 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2180 {
2181 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2182 }
2183 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2184 {
2185 CurrentProcess->Flags.Permanent = TRUE;
2186 }
2187 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2188 {
2189 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2190 }
2191 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2192 {
2193 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2194 }
2195
2196 /* These are turned on by default if no-execution is also eanbled */
2197 if (CurrentProcess->Flags.ExecuteEnable)
2198 {
2199 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2200 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2201 }
2202
2203 /* All good */
2204 Status = STATUS_SUCCESS;
2205 }
2206
2207 /* Release the lock and return status */
2208 KiReleaseProcessLock(&ProcessLock);
2209 return Status;
2210 }
2211
2212 /* EOF */