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