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