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