[NTOS/MM] Add Protection parameter to MiResolveDemandZeroFault
[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 /* Do we have the lock? */
673 if (HaveLock)
674 {
675 /* Release it */
676 MiReleasePfnLock(OldIrql);
677
678 /* Update performance counters */
679 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
680 }
681
682 /* Increment demand zero faults */
683 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
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 #if (_MI_PAGING_LEVELS == 4)
2029 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2030 // also this is missing the page count increment
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 #if 0
2037 /* Resolve a demand zero fault */
2038 Status = MiResolveDemandZeroFault(PointerPpe,
2039 PointerPxe,
2040 MM_READWRITE,
2041 CurrentProcess,
2042 MM_NOIRQL);
2043 #endif
2044 /* We should come back with a valid PXE */
2045 ASSERT(PointerPxe->u.Hard.Valid == 1);
2046 }
2047 #endif
2048
2049 #if (_MI_PAGING_LEVELS >= 3)
2050 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2051 // also this is missing the page count increment
2052 /* Check if the PPE is valid */
2053 if (PointerPpe->u.Hard.Valid == 0)
2054 {
2055 /* Right now, we only handle scenarios where the PPE is totally empty */
2056 ASSERT(PointerPpe->u.Long == 0);
2057 #if 0
2058 /* Resolve a demand zero fault */
2059 Status = MiResolveDemandZeroFault(PointerPde,
2060 PointerPpe,
2061 MM_READWRITE,
2062 CurrentProcess,
2063 MM_NOIRQL);
2064 #endif
2065 /* We should come back with a valid PPE */
2066 ASSERT(PointerPpe->u.Hard.Valid == 1);
2067 }
2068 #endif
2069
2070 /* Check if the PDE is invalid */
2071 if (PointerPde->u.Hard.Valid == 0)
2072 {
2073 /* Right now, we only handle scenarios where the PDE is totally empty */
2074 ASSERT(PointerPde->u.Long == 0);
2075
2076 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2077 #if MI_TRACE_PFNS
2078 UserPdeFault = TRUE;
2079 #endif
2080 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2081 if (ProtectionCode == MM_NOACCESS)
2082 {
2083 #if (_MI_PAGING_LEVELS == 2)
2084 /* Could be a page table for paged pool */
2085 MiCheckPdeForPagedPool(Address);
2086 #endif
2087 /* Has the code above changed anything -- is this now a valid PTE? */
2088 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2089
2090 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2091 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2092 return Status;
2093 }
2094
2095 /* Write a demand-zero PDE */
2096 MI_WRITE_INVALID_PDE(PointerPde, DemandZeroPde);
2097
2098 /* Dispatch the fault */
2099 Status = MiDispatchFault(TRUE,
2100 PointerPte,
2101 PointerPde,
2102 NULL,
2103 FALSE,
2104 PsGetCurrentProcess(),
2105 TrapInformation,
2106 NULL);
2107 #if MI_TRACE_PFNS
2108 UserPdeFault = FALSE;
2109 #endif
2110 /* We should come back with APCs enabled, and with a valid PDE */
2111 ASSERT(KeAreAllApcsDisabled() == TRUE);
2112 ASSERT(PointerPde->u.Hard.Valid == 1);
2113 }
2114 else
2115 {
2116 /* Not yet implemented in ReactOS */
2117 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
2118 }
2119
2120 /* Now capture the PTE. */
2121 TempPte = *PointerPte;
2122
2123 /* Check if the PTE is valid */
2124 if (TempPte.u.Hard.Valid)
2125 {
2126 /* Check if this is a write on a readonly PTE */
2127 if (StoreInstruction)
2128 {
2129 /* Is this a copy on write PTE? */
2130 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
2131 {
2132 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
2133 PMMPFN Pfn1;
2134
2135 LockIrql = MiAcquirePfnLock();
2136
2137 ASSERT(MmAvailablePages > 0);
2138
2139 /* Allocate a new page and copy it */
2140 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
2141 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
2142
2143 MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
2144
2145 /* Dereference whatever this PTE is referencing */
2146 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
2147 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
2148 ASSERT(!MI_IS_PFN_DELETED(Pfn1));
2149 ProtoPte = Pfn1->PteAddress;
2150 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
2151
2152 /* And make a new shiny one with our page */
2153 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
2154 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
2155 TempPte.u.Hard.Write = 1;
2156 TempPte.u.Hard.CopyOnWrite = 0;
2157
2158 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2159
2160 MiReleasePfnLock(LockIrql);
2161
2162 /* Return the status */
2163 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2164 return STATUS_PAGE_FAULT_COPY_ON_WRITE;
2165 }
2166
2167 /* Is this a read-only PTE? */
2168 if (!MI_IS_PAGE_WRITEABLE(&TempPte))
2169 {
2170 /* Return the status */
2171 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2172 return STATUS_ACCESS_VIOLATION;
2173 }
2174 }
2175
2176 /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
2177
2178 /* The fault has already been resolved by a different thread */
2179 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2180 return STATUS_SUCCESS;
2181 }
2182
2183 /* Quick check for demand-zero */
2184 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
2185 {
2186 /* Resolve the fault */
2187 MiResolveDemandZeroFault(Address,
2188 PointerPte,
2189 MM_READWRITE,
2190 CurrentProcess,
2191 MM_NOIRQL);
2192
2193 /* Return the status */
2194 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2195 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2196 }
2197
2198 /* Check for zero PTE */
2199 if (TempPte.u.Long == 0)
2200 {
2201 /* Check if this address range belongs to a valid allocation (VAD) */
2202 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2203 if (ProtectionCode == MM_NOACCESS)
2204 {
2205 #if (_MI_PAGING_LEVELS == 2)
2206 /* Could be a page table for paged pool */
2207 MiCheckPdeForPagedPool(Address);
2208 #endif
2209 /* Has the code above changed anything -- is this now a valid PTE? */
2210 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2211
2212 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2213 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2214 return Status;
2215 }
2216
2217 /*
2218 * Check if this is a real user-mode address or actually a kernel-mode
2219 * page table for a user mode address
2220 */
2221 if (Address <= MM_HIGHEST_USER_ADDRESS)
2222 {
2223 /* Add an additional page table reference */
2224 MiIncrementPageTableReferences(Address);
2225 }
2226
2227 /* Is this a guard page? */
2228 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
2229 {
2230 /* The VAD protection cannot be MM_DECOMMIT! */
2231 ASSERT(ProtectionCode != MM_DECOMMIT);
2232
2233 /* Remove the bit */
2234 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
2235 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2236
2237 /* Not supported */
2238 ASSERT(ProtoPte == NULL);
2239 ASSERT(CurrentThread->ApcNeeded == 0);
2240
2241 /* Drop the working set lock */
2242 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2243 ASSERT(KeGetCurrentIrql() == OldIrql);
2244
2245 /* Handle stack expansion */
2246 return MiCheckForUserStackOverflow(Address, TrapInformation);
2247 }
2248
2249 /* Did we get a prototype PTE back? */
2250 if (!ProtoPte)
2251 {
2252 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2253 if (PointerPde == MiAddressToPde(PTE_BASE))
2254 {
2255 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2256 #ifdef _M_ARM
2257 _WARN("This is probably completely broken!");
2258 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
2259 #else
2260 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
2261 #endif
2262 }
2263 else
2264 {
2265 /* No, create a new PTE. First, write the protection */
2266 TempPte.u.Soft.Protection = ProtectionCode;
2267 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2268 }
2269
2270 /* Lock the PFN database since we're going to grab a page */
2271 OldIrql = MiAcquirePfnLock();
2272
2273 /* Make sure we have enough pages */
2274 ASSERT(MmAvailablePages >= 32);
2275
2276 /* Try to get a zero page */
2277 MI_SET_USAGE(MI_USAGE_PEB_TEB);
2278 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
2279 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
2280 PageFrameIndex = MiRemoveZeroPageSafe(Color);
2281 if (!PageFrameIndex)
2282 {
2283 /* Grab a page out of there. Later we should grab a colored zero page */
2284 PageFrameIndex = MiRemoveAnyPage(Color);
2285 ASSERT(PageFrameIndex);
2286
2287 /* Release the lock since we need to do some zeroing */
2288 MiReleasePfnLock(OldIrql);
2289
2290 /* Zero out the page, since it's for user-mode */
2291 MiZeroPfn(PageFrameIndex);
2292
2293 /* Grab the lock again so we can initialize the PFN entry */
2294 OldIrql = MiAcquirePfnLock();
2295 }
2296
2297 /* Initialize the PFN entry now */
2298 MiInitializePfn(PageFrameIndex, PointerPte, 1);
2299
2300 /* And we're done with the lock */
2301 MiReleasePfnLock(OldIrql);
2302
2303 /* Increment the count of pages in the process */
2304 CurrentProcess->NumberOfPrivatePages++;
2305
2306 /* One more demand-zero fault */
2307 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
2308
2309 /* Fault on user PDE, or fault on user PTE? */
2310 if (PointerPte <= MiHighestUserPte)
2311 {
2312 /* User fault, build a user PTE */
2313 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2314 PointerPte,
2315 PointerPte->u.Soft.Protection,
2316 PageFrameIndex);
2317 }
2318 else
2319 {
2320 /* This is a user-mode PDE, create a kernel PTE for it */
2321 MI_MAKE_HARDWARE_PTE(&TempPte,
2322 PointerPte,
2323 PointerPte->u.Soft.Protection,
2324 PageFrameIndex);
2325 }
2326
2327 /* Write the dirty bit for writeable pages */
2328 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2329
2330 /* And now write down the PTE, making the address valid */
2331 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2332 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2333 ASSERT(Pfn1->u1.Event == NULL);
2334
2335 /* Demand zero */
2336 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2337 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2338 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2339 }
2340
2341 /* We should have a valid protection here */
2342 ASSERT(ProtectionCode != 0x100);
2343
2344 /* Write the prototype PTE */
2345 TempPte = PrototypePte;
2346 TempPte.u.Soft.Protection = ProtectionCode;
2347 ASSERT(TempPte.u.Long != 0);
2348 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2349 }
2350 else
2351 {
2352 /* Get the protection code and check if this is a proto PTE */
2353 ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2354 if (TempPte.u.Soft.Prototype)
2355 {
2356 /* Do we need to go find the real PTE? */
2357 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2358 {
2359 /* Get the prototype pte and VAD for it */
2360 ProtoPte = MiCheckVirtualAddress(Address,
2361 &ProtectionCode,
2362 &Vad);
2363 if (!ProtoPte)
2364 {
2365 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2366 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2367 return STATUS_ACCESS_VIOLATION;
2368 }
2369 }
2370 else
2371 {
2372 /* Get the prototype PTE! */
2373 ProtoPte = MiProtoPteToPte(&TempPte);
2374
2375 /* Is it read-only */
2376 if (TempPte.u.Proto.ReadOnly)
2377 {
2378 /* Set read-only code */
2379 ProtectionCode = MM_READONLY;
2380 }
2381 else
2382 {
2383 /* Set unknown protection */
2384 ProtectionCode = 0x100;
2385 ASSERT(CurrentProcess->CloneRoot != NULL);
2386 }
2387 }
2388 }
2389 }
2390
2391 /* Do we have a valid protection code? */
2392 if (ProtectionCode != 0x100)
2393 {
2394 /* Run a software access check first, including to detect guard pages */
2395 Status = MiAccessCheck(PointerPte,
2396 StoreInstruction,
2397 Mode,
2398 ProtectionCode,
2399 TrapInformation,
2400 FALSE);
2401 if (Status != STATUS_SUCCESS)
2402 {
2403 /* Not supported */
2404 ASSERT(CurrentThread->ApcNeeded == 0);
2405
2406 /* Drop the working set lock */
2407 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2408 ASSERT(KeGetCurrentIrql() == OldIrql);
2409
2410 /* Did we hit a guard page? */
2411 if (Status == STATUS_GUARD_PAGE_VIOLATION)
2412 {
2413 /* Handle stack expansion */
2414 return MiCheckForUserStackOverflow(Address, TrapInformation);
2415 }
2416
2417 /* Otherwise, fail back to the caller directly */
2418 return Status;
2419 }
2420 }
2421
2422 /* Dispatch the fault */
2423 Status = MiDispatchFault(StoreInstruction,
2424 Address,
2425 PointerPte,
2426 ProtoPte,
2427 FALSE,
2428 CurrentProcess,
2429 TrapInformation,
2430 Vad);
2431
2432 /* Return the status */
2433 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2434 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2435 return Status;
2436 }
2437
2438 NTSTATUS
2439 NTAPI
2440 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2441 {
2442 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2443 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2444
2445 *ExecuteOptions = 0;
2446
2447 if (CurrentProcess->Flags.ExecuteDisable)
2448 {
2449 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2450 }
2451
2452 if (CurrentProcess->Flags.ExecuteEnable)
2453 {
2454 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2455 }
2456
2457 if (CurrentProcess->Flags.DisableThunkEmulation)
2458 {
2459 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2460 }
2461
2462 if (CurrentProcess->Flags.Permanent)
2463 {
2464 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2465 }
2466
2467 if (CurrentProcess->Flags.ExecuteDispatchEnable)
2468 {
2469 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2470 }
2471
2472 if (CurrentProcess->Flags.ImageDispatchEnable)
2473 {
2474 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2475 }
2476
2477 return STATUS_SUCCESS;
2478 }
2479
2480 NTSTATUS
2481 NTAPI
2482 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2483 {
2484 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2485 KLOCK_QUEUE_HANDLE ProcessLock;
2486 NTSTATUS Status = STATUS_ACCESS_DENIED;
2487 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2488
2489 /* Only accept valid flags */
2490 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2491 {
2492 /* Fail */
2493 DPRINT1("Invalid no-execute options\n");
2494 return STATUS_INVALID_PARAMETER;
2495 }
2496
2497 /* Change the NX state in the process lock */
2498 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
2499
2500 /* Don't change anything if the permanent flag was set */
2501 if (!CurrentProcess->Flags.Permanent)
2502 {
2503 /* Start by assuming it's not disabled */
2504 CurrentProcess->Flags.ExecuteDisable = FALSE;
2505
2506 /* Now process each flag and turn the equivalent bit on */
2507 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2508 {
2509 CurrentProcess->Flags.ExecuteDisable = TRUE;
2510 }
2511 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2512 {
2513 CurrentProcess->Flags.ExecuteEnable = TRUE;
2514 }
2515 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2516 {
2517 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2518 }
2519 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2520 {
2521 CurrentProcess->Flags.Permanent = TRUE;
2522 }
2523 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2524 {
2525 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2526 }
2527 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2528 {
2529 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2530 }
2531
2532 /* These are turned on by default if no-execution is also eanbled */
2533 if (CurrentProcess->Flags.ExecuteEnable)
2534 {
2535 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2536 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2537 }
2538
2539 /* All good */
2540 Status = STATUS_SUCCESS;
2541 }
2542
2543 /* Release the lock and return status */
2544 KiReleaseProcessLock(&ProcessLock);
2545 return Status;
2546 }
2547
2548 /* EOF */