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