Sync with trunk r63430.
[reactos.git] / ntoskrnl / mm / ARM3 / pagfault.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #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 AccessAllowedMask[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 lower access bits */
138 ProtectionMask &= MM_PROTECT_ACCESS;
139
140 /* Look it up in the table */
141 return (AccessAllowedMask[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_PROTECT_SPECIAL) == 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 FORCEINLINE
315 BOOLEAN
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 special/caching bits */
769 Protection &= ~MM_PROTECT_SPECIAL;
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",
824 FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
825
826 /* Windowss does this check */
827 ASSERT(*InPageBlock == NULL);
828
829 /* ARM3 doesn't support this path */
830 ASSERT(OldIrql != MM_NOIRQL);
831
832 /* Capture the PTE and make sure it's in transition format */
833 TempPte = *PointerPte;
834 ASSERT((TempPte.u.Soft.Valid == 0) &&
835 (TempPte.u.Soft.Prototype == 0) &&
836 (TempPte.u.Soft.Transition == 1));
837
838 /* Get the PFN and the PFN entry */
839 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
840 DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
841 Pfn1 = MiGetPfnEntry(PageFrameIndex);
842
843 /* One more transition fault! */
844 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
845
846 /* This is from ARM3 -- Windows normally handles this here */
847 ASSERT(Pfn1->u4.InPageError == 0);
848
849 /* Not supported in ARM3 */
850 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
851
852 /* Windows checks there's some free pages and this isn't an in-page error */
853 ASSERT(MmAvailablePages > 0);
854 ASSERT(Pfn1->u4.InPageError == 0);
855
856 /* ReactOS checks for this */
857 ASSERT(MmAvailablePages > 32);
858
859 /* Was this a transition page in the valid list, or free/zero list? */
860 if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
861 {
862 /* All Windows does here is a bunch of sanity checks */
863 DPRINT1("Transition in active list\n");
864 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
865 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
866 ASSERT(Pfn1->u2.ShareCount != 0);
867 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
868 }
869 else
870 {
871 /* Otherwise, the page is removed from its list */
872 DPRINT1("Transition page in free/zero list\n");
873 MiUnlinkPageFromList(Pfn1);
874 MiReferenceUnusedPageAndBumpLockCount(Pfn1);
875 }
876
877 /* At this point, there should no longer be any in-page errors */
878 ASSERT(Pfn1->u4.InPageError == 0);
879
880 /* Check if this was a PFN with no more share references */
881 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
882
883 /* Bump the share count and make the page valid */
884 Pfn1->u2.ShareCount++;
885 Pfn1->u3.e1.PageLocation = ActiveAndValid;
886
887 /* Prototype PTEs are in paged pool, which itself might be in transition */
888 if (FaultingAddress >= MmSystemRangeStart)
889 {
890 /* Check if this is a paged pool PTE in transition state */
891 PointerToPteForProtoPage = MiAddressToPte(PointerPte);
892 TempPte = *PointerToPteForProtoPage;
893 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
894 {
895 /* This isn't yet supported */
896 DPRINT1("Double transition fault not yet supported\n");
897 ASSERT(FALSE);
898 }
899 }
900
901 /* Build the transition PTE -- maybe a macro? */
902 ASSERT(PointerPte->u.Hard.Valid == 0);
903 ASSERT(PointerPte->u.Trans.Prototype == 0);
904 ASSERT(PointerPte->u.Trans.Transition == 1);
905 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
906 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
907 MiDetermineUserGlobalPteMask(PointerPte);
908
909 /* Is the PTE writeable? */
910 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
911 (TempPte.u.Hard.CopyOnWrite == 0))
912 {
913 /* Make it dirty */
914 TempPte.u.Hard.Dirty = TRUE;
915 }
916 else
917 {
918 /* Make it clean */
919 TempPte.u.Hard.Dirty = FALSE;
920 }
921
922 /* Write the valid PTE */
923 MI_WRITE_VALID_PTE(PointerPte, TempPte);
924
925 /* Return success */
926 return STATUS_PAGE_FAULT_TRANSITION;
927 }
928
929 NTSTATUS
930 NTAPI
931 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
932 IN PVOID Address,
933 IN PMMPTE PointerPte,
934 IN PMMPTE PointerProtoPte,
935 IN OUT PMMPFN *OutPfn,
936 OUT PVOID *PageFileData,
937 OUT PMMPTE PteValue,
938 IN PEPROCESS Process,
939 IN KIRQL OldIrql,
940 IN PVOID TrapInformation)
941 {
942 MMPTE TempPte, PteContents;
943 PMMPFN Pfn1;
944 PFN_NUMBER PageFrameIndex;
945 NTSTATUS Status;
946 PVOID InPageBlock = NULL;
947
948 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
949 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
950 ASSERT(PointerPte->u.Hard.Valid == 0);
951 ASSERT(PointerPte->u.Soft.Prototype == 1);
952
953 /* Read the prototype PTE and check if it's valid */
954 TempPte = *PointerProtoPte;
955 if (TempPte.u.Hard.Valid == 1)
956 {
957 /* One more user of this mapped page */
958 PageFrameIndex = PFN_FROM_PTE(&TempPte);
959 Pfn1 = MiGetPfnEntry(PageFrameIndex);
960 Pfn1->u2.ShareCount++;
961
962 /* Call it a transition */
963 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
964
965 /* Complete the prototype PTE fault -- this will release the PFN lock */
966 return MiCompleteProtoPteFault(StoreInstruction,
967 Address,
968 PointerPte,
969 PointerProtoPte,
970 OldIrql,
971 OutPfn);
972 }
973
974 /* Make sure there's some protection mask */
975 if (TempPte.u.Long == 0)
976 {
977 /* Release the lock */
978 DPRINT1("Access on reserved section?\n");
979 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
980 return STATUS_ACCESS_VIOLATION;
981 }
982
983 /* There is no such thing as a decommitted prototype PTE */
984 NT_ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
985
986 /* Check for access rights on the PTE proper */
987 PteContents = *PointerPte;
988 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
989 {
990 if (!PteContents.u.Proto.ReadOnly)
991 {
992 /* Check for page acess in software */
993 Status = MiAccessCheck(PointerProtoPte,
994 StoreInstruction,
995 KernelMode,
996 TempPte.u.Soft.Protection,
997 TrapInformation,
998 TRUE);
999 ASSERT(Status == STATUS_SUCCESS);
1000
1001 /* Check for copy on write page */
1002 if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
1003 {
1004 /* Not yet supported */
1005 ASSERT(FALSE);
1006 }
1007 }
1008 }
1009 else
1010 {
1011 /* Check for copy on write page */
1012 if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
1013 {
1014 /* Not yet supported */
1015 ASSERT(FALSE);
1016 }
1017 }
1018
1019 /* Check for clone PTEs */
1020 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
1021
1022 /* We don't support mapped files yet */
1023 ASSERT(TempPte.u.Soft.Prototype == 0);
1024
1025 /* We might however have transition PTEs */
1026 if (TempPte.u.Soft.Transition == 1)
1027 {
1028 /* Resolve the transition fault */
1029 ASSERT(OldIrql != MM_NOIRQL);
1030 Status = MiResolveTransitionFault(Address,
1031 PointerProtoPte,
1032 Process,
1033 OldIrql,
1034 &InPageBlock);
1035 ASSERT(NT_SUCCESS(Status));
1036 }
1037 else
1038 {
1039 /* We also don't support paged out pages */
1040 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1041
1042 /* Resolve the demand zero fault */
1043 Status = MiResolveDemandZeroFault(Address,
1044 PointerProtoPte,
1045 Process,
1046 OldIrql);
1047 ASSERT(NT_SUCCESS(Status));
1048 }
1049
1050 /* Complete the prototype PTE fault -- this will release the PFN lock */
1051 ASSERT(PointerPte->u.Hard.Valid == 0);
1052 return MiCompleteProtoPteFault(StoreInstruction,
1053 Address,
1054 PointerPte,
1055 PointerProtoPte,
1056 OldIrql,
1057 OutPfn);
1058 }
1059
1060 NTSTATUS
1061 NTAPI
1062 MiDispatchFault(IN BOOLEAN StoreInstruction,
1063 IN PVOID Address,
1064 IN PMMPTE PointerPte,
1065 IN PMMPTE PointerProtoPte,
1066 IN BOOLEAN Recursive,
1067 IN PEPROCESS Process,
1068 IN PVOID TrapInformation,
1069 IN PMMVAD Vad)
1070 {
1071 MMPTE TempPte;
1072 KIRQL OldIrql, LockIrql;
1073 NTSTATUS Status;
1074 PMMPTE SuperProtoPte;
1075 PMMPFN Pfn1, OutPfn = NULL;
1076 PFN_NUMBER PageFrameIndex;
1077 PFN_COUNT PteCount, ProcessedPtes;
1078 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1079 Address,
1080 Process);
1081
1082 /* Make sure the addresses are ok */
1083 ASSERT(PointerPte == MiAddressToPte(Address));
1084
1085 //
1086 // Make sure APCs are off and we're not at dispatch
1087 //
1088 OldIrql = KeGetCurrentIrql();
1089 ASSERT(OldIrql <= APC_LEVEL);
1090 ASSERT(KeAreAllApcsDisabled() == TRUE);
1091
1092 //
1093 // Grab a copy of the PTE
1094 //
1095 TempPte = *PointerPte;
1096
1097 /* Do we have a prototype PTE? */
1098 if (PointerProtoPte)
1099 {
1100 /* This should never happen */
1101 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
1102
1103 /* Check if this is a kernel-mode address */
1104 SuperProtoPte = MiAddressToPte(PointerProtoPte);
1105 if (Address >= MmSystemRangeStart)
1106 {
1107 /* Lock the PFN database */
1108 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1109
1110 /* Has the PTE been made valid yet? */
1111 if (!SuperProtoPte->u.Hard.Valid)
1112 {
1113 ASSERT(FALSE);
1114 }
1115 else if (PointerPte->u.Hard.Valid == 1)
1116 {
1117 ASSERT(FALSE);
1118 }
1119
1120 /* Resolve the fault -- this will release the PFN lock */
1121 Status = MiResolveProtoPteFault(StoreInstruction,
1122 Address,
1123 PointerPte,
1124 PointerProtoPte,
1125 &OutPfn,
1126 NULL,
1127 NULL,
1128 Process,
1129 LockIrql,
1130 TrapInformation);
1131 ASSERT(Status == STATUS_SUCCESS);
1132
1133 /* Complete this as a transition fault */
1134 ASSERT(OldIrql == KeGetCurrentIrql());
1135 ASSERT(OldIrql <= APC_LEVEL);
1136 ASSERT(KeAreAllApcsDisabled() == TRUE);
1137 return Status;
1138 }
1139 else
1140 {
1141 /* We only handle the lookup path */
1142 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
1143
1144 /* Is there a non-image VAD? */
1145 if ((Vad) &&
1146 (Vad->u.VadFlags.VadType != VadImageMap) &&
1147 !(Vad->u2.VadFlags2.ExtendableFile))
1148 {
1149 /* One day, ReactOS will cluster faults */
1150 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
1151 DPRINT("Should cluster fault, but won't\n");
1152 }
1153
1154 /* Only one PTE to handle for now */
1155 PteCount = 1;
1156 ProcessedPtes = 0;
1157
1158 /* Lock the PFN database */
1159 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1160
1161 /* We only handle the valid path */
1162 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
1163
1164 /* Capture the PTE */
1165 TempPte = *PointerProtoPte;
1166
1167 /* Loop to handle future case of clustered faults */
1168 while (TRUE)
1169 {
1170 /* For our current usage, this should be true */
1171 if (TempPte.u.Hard.Valid == 1)
1172 {
1173 /* Bump the share count on the PTE */
1174 PageFrameIndex = PFN_FROM_PTE(&TempPte);
1175 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1176 Pfn1->u2.ShareCount++;
1177 }
1178 else if ((TempPte.u.Soft.Prototype == 0) &&
1179 (TempPte.u.Soft.Transition == 1))
1180 {
1181 /* This is a standby page, bring it back from the cache */
1182 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1183 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
1184 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1185 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1186
1187 /* Should not yet happen in ReactOS */
1188 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
1189 ASSERT(Pfn1->u4.InPageError == 0);
1190
1191 /* Get the page */
1192 MiUnlinkPageFromList(Pfn1);
1193
1194 /* Bump its reference count */
1195 ASSERT(Pfn1->u2.ShareCount == 0);
1196 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1197 Pfn1->u2.ShareCount++;
1198
1199 /* Make it valid again */
1200 /* This looks like another macro.... */
1201 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1202 ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1203 ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1204 ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1205 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1206 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1207 TempPte.u.Hard.Valid = 1;
1208 TempPte.u.Hard.Accessed = 1;
1209
1210 /* Is the PTE writeable? */
1211 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
1212 (TempPte.u.Hard.CopyOnWrite == 0))
1213 {
1214 /* Make it dirty */
1215 TempPte.u.Hard.Dirty = TRUE;
1216 }
1217 else
1218 {
1219 /* Make it clean */
1220 TempPte.u.Hard.Dirty = FALSE;
1221 }
1222
1223 /* Write the valid PTE */
1224 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1225 ASSERT(PointerPte->u.Hard.Valid == 0);
1226 }
1227 else
1228 {
1229 /* Page is invalid, get out of the loop */
1230 break;
1231 }
1232
1233 /* One more done, was it the last? */
1234 if (++ProcessedPtes == PteCount)
1235 {
1236 /* Complete the fault */
1237 MiCompleteProtoPteFault(StoreInstruction,
1238 Address,
1239 PointerPte,
1240 PointerProtoPte,
1241 LockIrql,
1242 &OutPfn);
1243
1244 /* THIS RELEASES THE PFN LOCK! */
1245 break;
1246 }
1247
1248 /* No clustered faults yet */
1249 ASSERT(FALSE);
1250 }
1251
1252 /* Did we resolve the fault? */
1253 if (ProcessedPtes)
1254 {
1255 /* Bump the transition count */
1256 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1257 ProcessedPtes--;
1258
1259 /* Loop all the processing we did */
1260 ASSERT(ProcessedPtes == 0);
1261
1262 /* Complete this as a transition fault */
1263 ASSERT(OldIrql == KeGetCurrentIrql());
1264 ASSERT(OldIrql <= APC_LEVEL);
1265 ASSERT(KeAreAllApcsDisabled() == TRUE);
1266 return STATUS_PAGE_FAULT_TRANSITION;
1267 }
1268
1269 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1270 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1271 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1272 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1273 ASSERT(PointerPte->u.Hard.Valid == 0);
1274
1275 /* Resolve the fault -- this will release the PFN lock */
1276 Status = MiResolveProtoPteFault(StoreInstruction,
1277 Address,
1278 PointerPte,
1279 PointerProtoPte,
1280 &OutPfn,
1281 NULL,
1282 NULL,
1283 Process,
1284 LockIrql,
1285 TrapInformation);
1286 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1287 //ASSERT(Status != STATUS_REFAULT);
1288 //ASSERT(Status != STATUS_PTE_CHANGED);
1289
1290 /* Did the routine clean out the PFN or should we? */
1291 if (OutPfn)
1292 {
1293 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1294 ASSERT(PointerProtoPte != NULL);
1295 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1296
1297 /* Dereference the locked PFN */
1298 MiDereferencePfnAndDropLockCount(OutPfn);
1299 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1300
1301 /* And now release the lock */
1302 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1303 }
1304
1305 /* Complete this as a transition fault */
1306 ASSERT(OldIrql == KeGetCurrentIrql());
1307 ASSERT(OldIrql <= APC_LEVEL);
1308 ASSERT(KeAreAllApcsDisabled() == TRUE);
1309 return Status;
1310 }
1311 }
1312
1313 //
1314 // The PTE must be invalid but not completely empty. It must also not be a
1315 // prototype PTE as that scenario should've been handled above. These are
1316 // all Windows checks
1317 //
1318 ASSERT(TempPte.u.Hard.Valid == 0);
1319 ASSERT(TempPte.u.Soft.Prototype == 0);
1320 ASSERT(TempPte.u.Long != 0);
1321
1322 //
1323 // No transition or page file software PTEs in ARM3 yet, so this must be a
1324 // demand zero page. These are all ReactOS checks
1325 //
1326 ASSERT(TempPte.u.Soft.Transition == 0);
1327 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1328
1329 //
1330 // If we got this far, the PTE can only be a demand zero PTE, which is what
1331 // we want. Go handle it!
1332 //
1333 Status = MiResolveDemandZeroFault(Address,
1334 PointerPte,
1335 Process,
1336 MM_NOIRQL);
1337 ASSERT(KeAreAllApcsDisabled() == TRUE);
1338 if (NT_SUCCESS(Status))
1339 {
1340 //
1341 // Make sure we're returning in a sane state and pass the status down
1342 //
1343 ASSERT(OldIrql == KeGetCurrentIrql());
1344 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1345 return Status;
1346 }
1347
1348 //
1349 // Generate an access fault
1350 //
1351 return STATUS_ACCESS_VIOLATION;
1352 }
1353
1354 NTSTATUS
1355 NTAPI
1356 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1357 IN PVOID Address,
1358 IN KPROCESSOR_MODE Mode,
1359 IN PVOID TrapInformation)
1360 {
1361 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1362 PMMPTE ProtoPte = NULL;
1363 PMMPTE PointerPte = MiAddressToPte(Address);
1364 PMMPDE PointerPde = MiAddressToPde(Address);
1365 #if (_MI_PAGING_LEVELS >= 3)
1366 PMMPDE PointerPpe = MiAddressToPpe(Address);
1367 #if (_MI_PAGING_LEVELS == 4)
1368 PMMPDE PointerPxe = MiAddressToPxe(Address);
1369 #endif
1370 #endif
1371 MMPTE TempPte;
1372 PETHREAD CurrentThread;
1373 PEPROCESS CurrentProcess;
1374 NTSTATUS Status;
1375 PMMSUPPORT WorkingSet;
1376 ULONG ProtectionCode;
1377 PMMVAD Vad;
1378 PFN_NUMBER PageFrameIndex;
1379 ULONG Color;
1380 BOOLEAN IsSessionAddress;
1381 PMMPFN Pfn1;
1382 DPRINT("ARM3 FAULT AT: %p\n", Address);
1383
1384 /* Check for page fault on high IRQL */
1385 if (OldIrql > APC_LEVEL)
1386 {
1387 #if (_MI_PAGING_LEVELS < 3)
1388 /* Could be a page table for paged pool, which we'll allow */
1389 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1390 MiCheckPdeForPagedPool(Address);
1391 #endif
1392 /* Check if any of the top-level pages are invalid */
1393 if (
1394 #if (_MI_PAGING_LEVELS == 4)
1395 (PointerPxe->u.Hard.Valid == 0) ||
1396 #endif
1397 #if (_MI_PAGING_LEVELS >= 3)
1398 (PointerPpe->u.Hard.Valid == 0) ||
1399 #endif
1400 (PointerPde->u.Hard.Valid == 0) ||
1401 (PointerPte->u.Hard.Valid == 0))
1402 {
1403 /* This fault is not valid, print out some debugging help */
1404 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1405 Address,
1406 OldIrql);
1407 if (TrapInformation)
1408 {
1409 PKTRAP_FRAME TrapFrame = TrapInformation;
1410 #ifdef _M_IX86
1411 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1412 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1413 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1414 #elif defined(_M_AMD64)
1415 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1416 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1417 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1418 #endif
1419 }
1420
1421 /* Tell the trap handler to fail */
1422 return STATUS_IN_PAGE_ERROR | 0x10000000;
1423 }
1424
1425 /* Not yet implemented in ReactOS */
1426 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1427 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1428
1429 /* Check if this was a write */
1430 if (StoreInstruction)
1431 {
1432 /* Was it to a read-only page? */
1433 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1434 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1435 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1436 {
1437 /* Crash with distinguished bugcheck code */
1438 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1439 (ULONG_PTR)Address,
1440 PointerPte->u.Long,
1441 (ULONG_PTR)TrapInformation,
1442 10);
1443 }
1444 }
1445
1446 /* Nothing is actually wrong */
1447 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
1448 return STATUS_SUCCESS;
1449 }
1450
1451 /* Check for kernel fault address */
1452 if (Address >= MmSystemRangeStart)
1453 {
1454 /* Bail out, if the fault came from user mode */
1455 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1456
1457 #if (_MI_PAGING_LEVELS == 2)
1458 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1459 MiCheckPdeForPagedPool(Address);
1460 #endif
1461
1462 /* Check if the higher page table entries are invalid */
1463 if (
1464 #if (_MI_PAGING_LEVELS == 4)
1465 /* AMD64 system, check if PXE is invalid */
1466 (PointerPxe->u.Hard.Valid == 0) ||
1467 #endif
1468 #if (_MI_PAGING_LEVELS >= 3)
1469 /* PAE/AMD64 system, check if PPE is invalid */
1470 (PointerPpe->u.Hard.Valid == 0) ||
1471 #endif
1472 /* Always check if the PDE is valid */
1473 (PointerPde->u.Hard.Valid == 0))
1474 {
1475 /* PXE/PPE/PDE (still) not valid, kill the system */
1476 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1477 (ULONG_PTR)Address,
1478 StoreInstruction,
1479 (ULONG_PTR)TrapInformation,
1480 2);
1481 }
1482
1483 /* Not handling session faults yet */
1484 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1485
1486 /* The PDE is valid, so read the PTE */
1487 TempPte = *PointerPte;
1488 if (TempPte.u.Hard.Valid == 1)
1489 {
1490 /* Check if this was system space or session space */
1491 if (!IsSessionAddress)
1492 {
1493 /* Check if the PTE is still valid under PFN lock */
1494 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1495 TempPte = *PointerPte;
1496 if (TempPte.u.Hard.Valid)
1497 {
1498 /* Check if this was a write */
1499 if (StoreInstruction)
1500 {
1501 /* Was it to a read-only page? */
1502 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1503 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1504 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1505 {
1506 /* Crash with distinguished bugcheck code */
1507 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1508 (ULONG_PTR)Address,
1509 PointerPte->u.Long,
1510 (ULONG_PTR)TrapInformation,
1511 11);
1512 }
1513 }
1514 }
1515
1516 /* Release PFN lock and return all good */
1517 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1518 return STATUS_SUCCESS;
1519 }
1520 }
1521 #if (_MI_PAGING_LEVELS == 2)
1522 /* Check if this was a session PTE that needs to remap the session PDE */
1523 if (MI_IS_SESSION_PTE(Address))
1524 {
1525 /* Do the remapping */
1526 Status = MiCheckPdeForSessionSpace(Address);
1527 if (!NT_SUCCESS(Status))
1528 {
1529 /* It failed, this address is invalid */
1530 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1531 (ULONG_PTR)Address,
1532 StoreInstruction,
1533 (ULONG_PTR)TrapInformation,
1534 6);
1535 }
1536 }
1537 #else
1538
1539 _WARN("Session space stuff is not implemented yet!")
1540
1541 #endif
1542
1543 /* Check for a fault on the page table or hyperspace */
1544 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1545 {
1546 #if (_MI_PAGING_LEVELS < 3)
1547 /* Windows does this check but I don't understand why -- it's done above! */
1548 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1549 #endif
1550 /* Handle this as a user mode fault */
1551 goto UserFault;
1552 }
1553
1554 /* Get the current thread */
1555 CurrentThread = PsGetCurrentThread();
1556
1557 /* What kind of address is this */
1558 if (!IsSessionAddress)
1559 {
1560 /* Use the system working set */
1561 WorkingSet = &MmSystemCacheWs;
1562 CurrentProcess = NULL;
1563
1564 /* Make sure we don't have a recursive working set lock */
1565 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1566 (CurrentThread->OwnsProcessWorkingSetShared) ||
1567 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1568 (CurrentThread->OwnsSystemWorkingSetShared) ||
1569 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1570 (CurrentThread->OwnsSessionWorkingSetShared))
1571 {
1572 /* Fail */
1573 return STATUS_IN_PAGE_ERROR | 0x10000000;
1574 }
1575 }
1576 else
1577 {
1578 /* Use the session process and working set */
1579 CurrentProcess = HYDRA_PROCESS;
1580 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1581
1582 /* Make sure we don't have a recursive working set lock */
1583 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1584 (CurrentThread->OwnsSessionWorkingSetShared))
1585 {
1586 /* Fail */
1587 return STATUS_IN_PAGE_ERROR | 0x10000000;
1588 }
1589 }
1590
1591 /* Acquire the working set lock */
1592 KeRaiseIrql(APC_LEVEL, &LockIrql);
1593 MiLockWorkingSet(CurrentThread, WorkingSet);
1594
1595 /* Re-read PTE now that we own the lock */
1596 TempPte = *PointerPte;
1597 if (TempPte.u.Hard.Valid == 1)
1598 {
1599 /* Check if this was a write */
1600 if (StoreInstruction)
1601 {
1602 /* Was it to a read-only page that is not copy on write? */
1603 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1604 if (!(TempPte.u.Long & PTE_READWRITE) &&
1605 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1606 !(TempPte.u.Hard.CopyOnWrite))
1607 {
1608 /* Case not yet handled */
1609 ASSERT(!IsSessionAddress);
1610
1611 /* Crash with distinguished bugcheck code */
1612 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1613 (ULONG_PTR)Address,
1614 TempPte.u.Long,
1615 (ULONG_PTR)TrapInformation,
1616 12);
1617 }
1618 }
1619
1620 /* Check for read-only write in session space */
1621 if ((IsSessionAddress) &&
1622 (StoreInstruction) &&
1623 !(TempPte.u.Hard.Write))
1624 {
1625 /* Sanity check */
1626 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1627
1628 /* Was this COW? */
1629 if (TempPte.u.Hard.CopyOnWrite == 0)
1630 {
1631 /* Then this is not allowed */
1632 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1633 (ULONG_PTR)Address,
1634 (ULONG_PTR)TempPte.u.Long,
1635 (ULONG_PTR)TrapInformation,
1636 13);
1637 }
1638
1639 /* Otherwise, handle COW */
1640 ASSERT(FALSE);
1641 }
1642
1643 /* Release the working set */
1644 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1645 KeLowerIrql(LockIrql);
1646
1647 /* Otherwise, the PDE was probably invalid, and all is good now */
1648 return STATUS_SUCCESS;
1649 }
1650
1651 /* Check one kind of prototype PTE */
1652 if (TempPte.u.Soft.Prototype)
1653 {
1654 /* Make sure protected pool is on, and that this is a pool address */
1655 if ((MmProtectFreedNonPagedPool) &&
1656 (((Address >= MmNonPagedPoolStart) &&
1657 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1658 MmSizeOfNonPagedPoolInBytes))) ||
1659 ((Address >= MmNonPagedPoolExpansionStart) &&
1660 (Address < MmNonPagedPoolEnd))))
1661 {
1662 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1663 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1664 (ULONG_PTR)Address,
1665 StoreInstruction,
1666 Mode,
1667 4);
1668 }
1669
1670 /* Get the prototype PTE! */
1671 ProtoPte = MiProtoPteToPte(&TempPte);
1672
1673 /* Do we need to locate the prototype PTE in session space? */
1674 if ((IsSessionAddress) &&
1675 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1676 {
1677 /* Yep, go find it as well as the VAD for it */
1678 ProtoPte = MiCheckVirtualAddress(Address,
1679 &ProtectionCode,
1680 &Vad);
1681 ASSERT(ProtoPte != NULL);
1682 }
1683 }
1684 else
1685 {
1686 /* We don't implement transition PTEs */
1687 ASSERT(TempPte.u.Soft.Transition == 0);
1688
1689 /* Check for no-access PTE */
1690 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1691 {
1692 /* Bugcheck the system! */
1693 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1694 (ULONG_PTR)Address,
1695 StoreInstruction,
1696 (ULONG_PTR)TrapInformation,
1697 1);
1698 }
1699 }
1700
1701 /* Check for demand page */
1702 if ((StoreInstruction) &&
1703 !(ProtoPte) &&
1704 !(IsSessionAddress) &&
1705 !(TempPte.u.Hard.Valid))
1706 {
1707 /* Get the protection code */
1708 ASSERT(TempPte.u.Soft.Transition == 0);
1709 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1710 {
1711 /* Bugcheck the system! */
1712 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1713 (ULONG_PTR)Address,
1714 TempPte.u.Long,
1715 (ULONG_PTR)TrapInformation,
1716 14);
1717 }
1718 }
1719
1720 /* Now do the real fault handling */
1721 Status = MiDispatchFault(StoreInstruction,
1722 Address,
1723 PointerPte,
1724 ProtoPte,
1725 FALSE,
1726 CurrentProcess,
1727 TrapInformation,
1728 NULL);
1729
1730 /* Release the working set */
1731 ASSERT(KeAreAllApcsDisabled() == TRUE);
1732 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1733 KeLowerIrql(LockIrql);
1734
1735 /* We are done! */
1736 DPRINT("Fault resolved with status: %lx\n", Status);
1737 return Status;
1738 }
1739
1740 /* This is a user fault */
1741 UserFault:
1742 CurrentThread = PsGetCurrentThread();
1743 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1744
1745 /* Lock the working set */
1746 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1747
1748 #if (_MI_PAGING_LEVELS == 4)
1749 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1750 // also this is missing the page count increment
1751 /* Check if the PXE is valid */
1752 if (PointerPxe->u.Hard.Valid == 0)
1753 {
1754 /* Right now, we only handle scenarios where the PXE is totally empty */
1755 ASSERT(PointerPxe->u.Long == 0);
1756 #if 0
1757 /* Resolve a demand zero fault */
1758 Status = MiResolveDemandZeroFault(PointerPpe,
1759 MM_READWRITE,
1760 CurrentProcess,
1761 MM_NOIRQL);
1762 #endif
1763 /* We should come back with a valid PXE */
1764 ASSERT(PointerPxe->u.Hard.Valid == 1);
1765 }
1766 #endif
1767
1768 #if (_MI_PAGING_LEVELS >= 3)
1769 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1770 // also this is missing the page count increment
1771 /* Check if the PPE is valid */
1772 if (PointerPpe->u.Hard.Valid == 0)
1773 {
1774 /* Right now, we only handle scenarios where the PPE is totally empty */
1775 ASSERT(PointerPpe->u.Long == 0);
1776 #if 0
1777 /* Resolve a demand zero fault */
1778 Status = MiResolveDemandZeroFault(PointerPde,
1779 MM_READWRITE,
1780 CurrentProcess,
1781 MM_NOIRQL);
1782 #endif
1783 /* We should come back with a valid PPE */
1784 ASSERT(PointerPpe->u.Hard.Valid == 1);
1785 }
1786 #endif
1787
1788 /* Check if the PDE is valid */
1789 if (PointerPde->u.Hard.Valid == 0)
1790 {
1791 /* Right now, we only handle scenarios where the PDE is totally empty */
1792 ASSERT(PointerPde->u.Long == 0);
1793
1794 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1795 #if MI_TRACE_PFNS
1796 UserPdeFault = TRUE;
1797 #endif
1798 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1799 if (ProtectionCode == MM_NOACCESS)
1800 {
1801 #if (_MI_PAGING_LEVELS == 2)
1802 /* Could be a page table for paged pool */
1803 MiCheckPdeForPagedPool(Address);
1804 #endif
1805 /* Has the code above changed anything -- is this now a valid PTE? */
1806 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1807
1808 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1809 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1810 return Status;
1811 }
1812
1813 /* Write a demand-zero PDE */
1814 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1815
1816 /* Dispatch the fault */
1817 Status = MiDispatchFault(TRUE,
1818 PointerPte,
1819 PointerPde,
1820 NULL,
1821 FALSE,
1822 PsGetCurrentProcess(),
1823 TrapInformation,
1824 NULL);
1825 #if MI_TRACE_PFNS
1826 UserPdeFault = FALSE;
1827 #endif
1828 /* We should come back with APCs enabled, and with a valid PDE */
1829 ASSERT(KeAreAllApcsDisabled() == TRUE);
1830 ASSERT(PointerPde->u.Hard.Valid == 1);
1831 }
1832 else
1833 {
1834 /* Not yet implemented in ReactOS */
1835 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1836 }
1837
1838 /* Now capture the PTE. */
1839 TempPte = *PointerPte;
1840
1841 /* Check if the PTE is valid */
1842 if (TempPte.u.Hard.Valid)
1843 {
1844 /* Check if this is a write on a readonly PTE */
1845 if (StoreInstruction)
1846 {
1847 /* Is this a copy on write PTE? */
1848 if (TempPte.u.Hard.CopyOnWrite)
1849 {
1850 /* Not supported yet */
1851 ASSERT(FALSE);
1852 }
1853
1854 /* Is this a read-only PTE? */
1855 if (!TempPte.u.Hard.Write)
1856 {
1857 /* Return the status */
1858 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1859 return STATUS_ACCESS_VIOLATION;
1860 }
1861 }
1862
1863 /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
1864
1865 /* The fault has already been resolved by a different thread */
1866 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1867 return STATUS_SUCCESS;
1868 }
1869
1870 /* Quick check for demand-zero */
1871 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1872 {
1873 /* Resolve the fault */
1874 MiResolveDemandZeroFault(Address,
1875 PointerPte,
1876 CurrentProcess,
1877 MM_NOIRQL);
1878
1879 /* Return the status */
1880 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1881 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1882 }
1883
1884 /* Check for zero PTE */
1885 if (TempPte.u.Long == 0)
1886 {
1887 /* Check if this address range belongs to a valid allocation (VAD) */
1888 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1889 if (ProtectionCode == MM_NOACCESS)
1890 {
1891 #if (_MI_PAGING_LEVELS == 2)
1892 /* Could be a page table for paged pool */
1893 MiCheckPdeForPagedPool(Address);
1894 #endif
1895 /* Has the code above changed anything -- is this now a valid PTE? */
1896 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1897
1898 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1899 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1900 return Status;
1901 }
1902
1903 /*
1904 * Check if this is a real user-mode address or actually a kernel-mode
1905 * page table for a user mode address
1906 */
1907 if (Address <= MM_HIGHEST_USER_ADDRESS)
1908 {
1909 /* Add an additional page table reference */
1910 MiIncrementPageTableReferences(Address);
1911 }
1912
1913 /* Is this a guard page? */
1914 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
1915 {
1916 /* The VAD protection cannot be MM_DECOMMIT! */
1917 NT_ASSERT(ProtectionCode != MM_DECOMMIT);
1918
1919 /* Remove the bit */
1920 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
1921 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1922
1923 /* Not supported */
1924 ASSERT(ProtoPte == NULL);
1925 ASSERT(CurrentThread->ApcNeeded == 0);
1926
1927 /* Drop the working set lock */
1928 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1929 ASSERT(KeGetCurrentIrql() == OldIrql);
1930
1931 /* Handle stack expansion */
1932 return MiCheckForUserStackOverflow(Address, TrapInformation);
1933 }
1934
1935 /* Did we get a prototype PTE back? */
1936 if (!ProtoPte)
1937 {
1938 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1939 if (PointerPde == MiAddressToPde(PTE_BASE))
1940 {
1941 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1942 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1943 }
1944 else
1945 {
1946 /* No, create a new PTE. First, write the protection */
1947 TempPte.u.Soft.Protection = ProtectionCode;
1948 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1949 }
1950
1951 /* Lock the PFN database since we're going to grab a page */
1952 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1953
1954 /* Make sure we have enough pages */
1955 ASSERT(MmAvailablePages >= 32);
1956
1957 /* Try to get a zero page */
1958 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1959 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1960 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1961 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1962 if (!PageFrameIndex)
1963 {
1964 /* Grab a page out of there. Later we should grab a colored zero page */
1965 PageFrameIndex = MiRemoveAnyPage(Color);
1966 ASSERT(PageFrameIndex);
1967
1968 /* Release the lock since we need to do some zeroing */
1969 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1970
1971 /* Zero out the page, since it's for user-mode */
1972 MiZeroPfn(PageFrameIndex);
1973
1974 /* Grab the lock again so we can initialize the PFN entry */
1975 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1976 }
1977
1978 /* Initialize the PFN entry now */
1979 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1980
1981 /* And we're done with the lock */
1982 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1983
1984 /* Increment the count of pages in the process */
1985 CurrentProcess->NumberOfPrivatePages++;
1986
1987 /* One more demand-zero fault */
1988 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1989
1990 /* Fault on user PDE, or fault on user PTE? */
1991 if (PointerPte <= MiHighestUserPte)
1992 {
1993 /* User fault, build a user PTE */
1994 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1995 PointerPte,
1996 PointerPte->u.Soft.Protection,
1997 PageFrameIndex);
1998 }
1999 else
2000 {
2001 /* This is a user-mode PDE, create a kernel PTE for it */
2002 MI_MAKE_HARDWARE_PTE(&TempPte,
2003 PointerPte,
2004 PointerPte->u.Soft.Protection,
2005 PageFrameIndex);
2006 }
2007
2008 /* Write the dirty bit for writeable pages */
2009 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2010
2011 /* And now write down the PTE, making the address valid */
2012 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2013 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2014 ASSERT(Pfn1->u1.Event == NULL);
2015
2016 /* Demand zero */
2017 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2018 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2019 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2020 }
2021
2022 /* We should have a valid protection here */
2023 ASSERT(ProtectionCode != 0x100);
2024
2025 /* Write the prototype PTE */
2026 TempPte = PrototypePte;
2027 TempPte.u.Soft.Protection = ProtectionCode;
2028 NT_ASSERT(TempPte.u.Long != 0);
2029 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2030 }
2031 else
2032 {
2033 /* Get the protection code and check if this is a proto PTE */
2034 ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2035 if (TempPte.u.Soft.Prototype)
2036 {
2037 /* Do we need to go find the real PTE? */
2038 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2039 {
2040 /* Get the prototype pte and VAD for it */
2041 ProtoPte = MiCheckVirtualAddress(Address,
2042 &ProtectionCode,
2043 &Vad);
2044 if (!ProtoPte)
2045 {
2046 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2047 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2048 return STATUS_ACCESS_VIOLATION;
2049 }
2050 }
2051 else
2052 {
2053 /* Get the prototype PTE! */
2054 ProtoPte = MiProtoPteToPte(&TempPte);
2055
2056 /* Is it read-only */
2057 if (TempPte.u.Proto.ReadOnly)
2058 {
2059 /* Set read-only code */
2060 ProtectionCode = MM_READONLY;
2061 }
2062 else
2063 {
2064 /* Set unknown protection */
2065 ProtectionCode = 0x100;
2066 ASSERT(CurrentProcess->CloneRoot != NULL);
2067 }
2068 }
2069 }
2070 }
2071
2072 /* Do we have a valid protection code? */
2073 if (ProtectionCode != 0x100)
2074 {
2075 /* Run a software access check first, including to detect guard pages */
2076 Status = MiAccessCheck(PointerPte,
2077 StoreInstruction,
2078 Mode,
2079 ProtectionCode,
2080 TrapInformation,
2081 FALSE);
2082 if (Status != STATUS_SUCCESS)
2083 {
2084 /* Not supported */
2085 ASSERT(CurrentThread->ApcNeeded == 0);
2086
2087 /* Drop the working set lock */
2088 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2089 ASSERT(KeGetCurrentIrql() == OldIrql);
2090
2091 /* Did we hit a guard page? */
2092 if (Status == STATUS_GUARD_PAGE_VIOLATION)
2093 {
2094 /* Handle stack expansion */
2095 return MiCheckForUserStackOverflow(Address, TrapInformation);
2096 }
2097
2098 /* Otherwise, fail back to the caller directly */
2099 return Status;
2100 }
2101 }
2102
2103 /* Dispatch the fault */
2104 Status = MiDispatchFault(StoreInstruction,
2105 Address,
2106 PointerPte,
2107 ProtoPte,
2108 FALSE,
2109 CurrentProcess,
2110 TrapInformation,
2111 Vad);
2112
2113 /* Return the status */
2114 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2115 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2116 return Status;
2117 }
2118
2119 NTSTATUS
2120 NTAPI
2121 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2122 {
2123 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2124 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2125
2126 *ExecuteOptions = 0;
2127
2128 if (CurrentProcess->Flags.ExecuteDisable)
2129 {
2130 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2131 }
2132
2133 if (CurrentProcess->Flags.ExecuteEnable)
2134 {
2135 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2136 }
2137
2138 if (CurrentProcess->Flags.DisableThunkEmulation)
2139 {
2140 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2141 }
2142
2143 if (CurrentProcess->Flags.Permanent)
2144 {
2145 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2146 }
2147
2148 if (CurrentProcess->Flags.ExecuteDispatchEnable)
2149 {
2150 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2151 }
2152
2153 if (CurrentProcess->Flags.ImageDispatchEnable)
2154 {
2155 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2156 }
2157
2158 return STATUS_SUCCESS;
2159 }
2160
2161 NTSTATUS
2162 NTAPI
2163 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2164 {
2165 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2166 KLOCK_QUEUE_HANDLE ProcessLock;
2167 NTSTATUS Status = STATUS_ACCESS_DENIED;
2168 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2169
2170 /* Only accept valid flags */
2171 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2172 {
2173 /* Fail */
2174 DPRINT1("Invalid no-execute options\n");
2175 return STATUS_INVALID_PARAMETER;
2176 }
2177
2178 /* Change the NX state in the process lock */
2179 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
2180
2181 /* Don't change anything if the permanent flag was set */
2182 if (!CurrentProcess->Flags.Permanent)
2183 {
2184 /* Start by assuming it's not disabled */
2185 CurrentProcess->Flags.ExecuteDisable = FALSE;
2186
2187 /* Now process each flag and turn the equivalent bit on */
2188 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2189 {
2190 CurrentProcess->Flags.ExecuteDisable = TRUE;
2191 }
2192 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2193 {
2194 CurrentProcess->Flags.ExecuteEnable = TRUE;
2195 }
2196 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2197 {
2198 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2199 }
2200 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2201 {
2202 CurrentProcess->Flags.Permanent = TRUE;
2203 }
2204 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2205 {
2206 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2207 }
2208 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2209 {
2210 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2211 }
2212
2213 /* These are turned on by default if no-execution is also eanbled */
2214 if (CurrentProcess->Flags.ExecuteEnable)
2215 {
2216 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2217 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2218 }
2219
2220 /* All good */
2221 Status = STATUS_SUCCESS;
2222 }
2223
2224 /* Release the lock and return status */
2225 KiReleaseProcessLock(&ProcessLock);
2226 return Status;
2227 }
2228
2229 /* EOF */