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