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