[NTOSKRNL]: Fix typo.
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / pagfault.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pagfault.c
5 * PURPOSE: ARM Memory Manager Page Fault Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
17
18 /* GLOBALS ********************************************************************/
19
20 #define HYDRA_PROCESS (PEPROCESS)1
21 #if MI_TRACE_PFNS
22 BOOLEAN UserPdeFault = FALSE;
23 #endif
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 PMMPTE
28 NTAPI
29 MiCheckVirtualAddress(IN PVOID VirtualAddress,
30 OUT PULONG ProtectCode,
31 OUT PMMVAD *ProtoVad)
32 {
33 PMMVAD Vad;
34 PMMPTE PointerPte;
35
36 /* No prototype/section support for now */
37 *ProtoVad = NULL;
38
39 /* User or kernel fault? */
40 if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS)
41 {
42 /* Special case for shared data */
43 if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
44 {
45 /* It's a read-only page */
46 *ProtectCode = MM_READONLY;
47 return MmSharedUserDataPte;
48 }
49
50 /* Find the VAD, it might not exist if the address is bogus */
51 Vad = MiLocateAddress(VirtualAddress);
52 if (!Vad)
53 {
54 /* Bogus virtual address */
55 *ProtectCode = MM_NOACCESS;
56 return NULL;
57 }
58
59 /* ReactOS does not handle physical memory VADs yet */
60 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
61
62 /* Check if it's a section, or just an allocation */
63 if (Vad->u.VadFlags.PrivateMemory)
64 {
65 /* ReactOS does not handle AWE VADs yet */
66 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
67
68 /* This must be a TEB/PEB VAD */
69 if (Vad->u.VadFlags.MemCommit)
70 {
71 /* It's committed, so return the VAD protection */
72 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
73 }
74 else
75 {
76 /* It has not yet been committed, so return no access */
77 *ProtectCode = MM_NOACCESS;
78 }
79
80 /* In both cases, return no PTE */
81 return NULL;
82 }
83 else
84 {
85 /* ReactOS does not supoprt these VADs yet */
86 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
87 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
88
89 /* Return the proto VAD */
90 *ProtoVad = Vad;
91
92 /* Get the prototype PTE for this page */
93 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
94 ASSERT(PointerPte != NULL);
95 ASSERT(PointerPte <= Vad->LastContiguousPte);
96
97 /* Return the Prototype PTE and the protection for the page mapping */
98 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
99 return PointerPte;
100 }
101 }
102 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
103 {
104 /* This should never happen, as these addresses are handled by the double-maping */
105 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
106 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
107 {
108 /* Fail such access */
109 *ProtectCode = MM_NOACCESS;
110 return NULL;
111 }
112
113 /* Return full access rights */
114 *ProtectCode = MM_READWRITE;
115 return NULL;
116 }
117 else if (MI_IS_SESSION_ADDRESS(VirtualAddress))
118 {
119 /* ReactOS does not have an image list yet, so bail out to failure case */
120 ASSERT(IsListEmpty(&MmSessionSpace->ImageList));
121 }
122
123 /* Default case -- failure */
124 *ProtectCode = MM_NOACCESS;
125 return NULL;
126 }
127
128 #if (_MI_PAGING_LEVELS == 2)
129 BOOLEAN
130 FORCEINLINE
131 MiSynchronizeSystemPde(PMMPDE PointerPde)
132 {
133 MMPDE SystemPde;
134 ULONG Index;
135
136 /* Get the Index from the PDE */
137 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
138
139 /* Copy the PDE from the double-mapped system page directory */
140 SystemPde = MmSystemPagePtes[Index];
141 *PointerPde = SystemPde;
142
143 /* Make sure we re-read the PDE and PTE */
144 KeMemoryBarrierWithoutFence();
145
146 /* Return, if we had success */
147 return (BOOLEAN)SystemPde.u.Hard.Valid;
148 }
149
150 NTSTATUS
151 FASTCALL
152 MiCheckPdeForSessionSpace(IN PVOID Address)
153 {
154 MMPTE TempPde;
155 PMMPTE PointerPde;
156 PVOID SessionPageTable;
157 ULONG Index;
158
159 /* Is this a session PTE? */
160 if (MI_IS_SESSION_PTE(Address))
161 {
162 /* Make sure the PDE for session space is valid */
163 PointerPde = MiAddressToPde(MmSessionSpace);
164 if (!PointerPde->u.Hard.Valid)
165 {
166 /* This means there's no valid session, bail out */
167 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
168 Address);
169 DbgBreakPoint();
170 return STATUS_ACCESS_VIOLATION;
171 }
172
173 /* Now get the session-specific page table for this address */
174 SessionPageTable = MiPteToAddress(Address);
175 PointerPde = MiPteToAddress(Address);
176 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
177
178 /* It's not valid, so find it in the page table array */
179 Index = ((ULONG_PTR)SessionPageTable - (ULONG_PTR)MmSessionBase) >> 22;
180 TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
181 if (TempPde.u.Hard.Valid)
182 {
183 /* The copy is valid, so swap it in */
184 InterlockedExchange((PLONG)PointerPde, TempPde.u.Long);
185 return STATUS_WAIT_1;
186 }
187
188 /* We don't seem to have allocated a page table for this address yet? */
189 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
190 PointerPde->u.Long, SessionPageTable);
191 DbgBreakPoint();
192 return STATUS_ACCESS_VIOLATION;
193 }
194
195 /* Is the address also a session address? If not, we're done */
196 if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS;
197
198 /* It is, so again get the PDE for session space */
199 PointerPde = MiAddressToPde(MmSessionSpace);
200 if (!PointerPde->u.Hard.Valid)
201 {
202 /* This means there's no valid session, bail out */
203 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
204 Address);
205 DbgBreakPoint();
206 return STATUS_ACCESS_VIOLATION;
207 }
208
209 /* Now get the PDE for the address itself */
210 PointerPde = MiAddressToPde(Address);
211 if (!PointerPde->u.Hard.Valid)
212 {
213 /* Do the swap, we should be good to go */
214 Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22;
215 PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
216 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
217
218 /* We had not allocated a page table for this session address yet, fail! */
219 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
220 PointerPde->u.Long, Address);
221 DbgBreakPoint();
222 return STATUS_ACCESS_VIOLATION;
223 }
224
225 /* It's valid, so there's nothing to do */
226 return STATUS_SUCCESS;
227 }
228
229 NTSTATUS
230 FASTCALL
231 MiCheckPdeForPagedPool(IN PVOID Address)
232 {
233 PMMPDE PointerPde;
234 NTSTATUS Status = STATUS_SUCCESS;
235
236 /* Check session PDE */
237 if (MI_IS_SESSION_ADDRESS(Address)) return MiCheckPdeForSessionSpace(Address);
238 if (MI_IS_SESSION_PTE(Address)) return MiCheckPdeForSessionSpace(Address);
239
240 //
241 // Check if this is a fault while trying to access the page table itself
242 //
243 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
244 {
245 //
246 // Send a hint to the page fault handler that this is only a valid fault
247 // if we already detected this was access within the page table range
248 //
249 PointerPde = (PMMPDE)MiAddressToPte(Address);
250 Status = STATUS_WAIT_1;
251 }
252 else if (Address < MmSystemRangeStart)
253 {
254 //
255 // This is totally illegal
256 //
257 return STATUS_ACCESS_VIOLATION;
258 }
259 else
260 {
261 //
262 // Get the PDE for the address
263 //
264 PointerPde = MiAddressToPde(Address);
265 }
266
267 //
268 // Check if it's not valid
269 //
270 if (PointerPde->u.Hard.Valid == 0)
271 {
272 //
273 // Copy it from our double-mapped system page directory
274 //
275 InterlockedExchangePte(PointerPde,
276 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
277 }
278
279 //
280 // Return status
281 //
282 return Status;
283 }
284 #else
285 NTSTATUS
286 FASTCALL
287 MiCheckPdeForPagedPool(IN PVOID Address)
288 {
289 return STATUS_ACCESS_VIOLATION;
290 }
291 #endif
292
293 VOID
294 NTAPI
295 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
296 {
297 PMMPTE ZeroPte;
298 MMPTE TempPte;
299 PMMPFN Pfn1;
300 PVOID ZeroAddress;
301
302 /* Get the PFN for this page */
303 Pfn1 = MiGetPfnEntry(PageFrameNumber);
304 ASSERT(Pfn1);
305
306 /* Grab a system PTE we can use to zero the page */
307 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
308 ASSERT(ZeroPte);
309
310 /* Initialize the PTE for it */
311 TempPte = ValidKernelPte;
312 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
313
314 /* Setup caching */
315 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
316 {
317 /* Write combining, no caching */
318 MI_PAGE_DISABLE_CACHE(&TempPte);
319 MI_PAGE_WRITE_COMBINED(&TempPte);
320 }
321 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
322 {
323 /* Write through, no caching */
324 MI_PAGE_DISABLE_CACHE(&TempPte);
325 MI_PAGE_WRITE_THROUGH(&TempPte);
326 }
327
328 /* Make the system PTE valid with our PFN */
329 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
330
331 /* Get the address it maps to, and zero it out */
332 ZeroAddress = MiPteToAddress(ZeroPte);
333 KeZeroPages(ZeroAddress, PAGE_SIZE);
334
335 /* Now get rid of it */
336 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
337 }
338
339 NTSTATUS
340 NTAPI
341 MiResolveDemandZeroFault(IN PVOID Address,
342 IN PMMPTE PointerPte,
343 IN PEPROCESS Process,
344 IN KIRQL OldIrql)
345 {
346 PFN_NUMBER PageFrameNumber = 0;
347 MMPTE TempPte;
348 BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
349 ULONG Color;
350 PMMPFN Pfn1;
351 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
352 Address,
353 Process);
354
355 /* Must currently only be called by paging path */
356 if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL))
357 {
358 /* Sanity check */
359 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
360
361 /* No forking yet */
362 ASSERT(Process->ForkInProgress == NULL);
363
364 /* Get process color */
365 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
366 ASSERT(Color != 0xFFFFFFFF);
367
368 /* We'll need a zero page */
369 NeedZero = TRUE;
370 }
371 else
372 {
373 /* Check if we need a zero page */
374 NeedZero = (OldIrql != MM_NOIRQL);
375
376 /* Session-backed image views must be zeroed */
377 if ((Process == HYDRA_PROCESS) &&
378 ((MI_IS_SESSION_IMAGE_ADDRESS(Address)) ||
379 ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs))))
380 {
381 NeedZero = TRUE;
382 }
383
384 /* Hardcode unknown color */
385 Color = 0xFFFFFFFF;
386 }
387
388 /* Check if the PFN database should be acquired */
389 if (OldIrql == MM_NOIRQL)
390 {
391 /* Acquire it and remember we should release it after */
392 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
393 HaveLock = TRUE;
394 }
395
396 /* We either manually locked the PFN DB, or already came with it locked */
397 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
398 ASSERT(PointerPte->u.Hard.Valid == 0);
399
400 /* Assert we have enough pages */
401 ASSERT(MmAvailablePages >= 32);
402
403 #if MI_TRACE_PFNS
404 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
405 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
406 #endif
407 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
408 if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
409
410 /* Do we need a zero page? */
411 if (Color != 0xFFFFFFFF)
412 {
413 /* Try to get one, if we couldn't grab a free page and zero it */
414 PageFrameNumber = MiRemoveZeroPageSafe(Color);
415 if (!PageFrameNumber)
416 {
417 /* We'll need a free page and zero it manually */
418 PageFrameNumber = MiRemoveAnyPage(Color);
419 NeedZero = TRUE;
420 }
421 }
422 else
423 {
424 /* Get a color, and see if we should grab a zero or non-zero page */
425 Color = MI_GET_NEXT_COLOR();
426 if (!NeedZero)
427 {
428 /* Process or system doesn't want a zero page, grab anything */
429 PageFrameNumber = MiRemoveAnyPage(Color);
430 }
431 else
432 {
433 /* System wants a zero page, obtain one */
434 PageFrameNumber = MiRemoveZeroPage(Color);
435 }
436 }
437
438 /* Initialize it */
439 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
440
441 /* Do we have the lock? */
442 if (HaveLock)
443 {
444 /* Release it */
445 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
446
447 /* Update performance counters */
448 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
449 }
450
451 /* Increment demand zero faults */
452 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
453
454 /* Zero the page if need be */
455 if (NeedZero) MiZeroPfn(PageFrameNumber);
456
457 /* Fault on user PDE, or fault on user PTE? */
458 if (PointerPte <= MiHighestUserPte)
459 {
460 /* User fault, build a user PTE */
461 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
462 PointerPte,
463 PointerPte->u.Soft.Protection,
464 PageFrameNumber);
465 }
466 else
467 {
468 /* This is a user-mode PDE, create a kernel PTE for it */
469 MI_MAKE_HARDWARE_PTE(&TempPte,
470 PointerPte,
471 PointerPte->u.Soft.Protection,
472 PageFrameNumber);
473 }
474
475 /* Set it dirty if it's a writable page */
476 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
477
478 /* Write it */
479 MI_WRITE_VALID_PTE(PointerPte, TempPte);
480
481 /* Did we manually acquire the lock */
482 if (HaveLock)
483 {
484 /* Get the PFN entry */
485 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
486
487 /* Windows does these sanity checks */
488 ASSERT(Pfn1->u1.Event == 0);
489 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
490 }
491
492 //
493 // It's all good now
494 //
495 DPRINT("Demand zero page has now been paged in\n");
496 return STATUS_PAGE_FAULT_DEMAND_ZERO;
497 }
498
499 NTSTATUS
500 NTAPI
501 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
502 IN PVOID Address,
503 IN PMMPTE PointerPte,
504 IN PMMPTE PointerProtoPte,
505 IN KIRQL OldIrql,
506 IN PMMPFN* LockedProtoPfn)
507 {
508 MMPTE TempPte;
509 PMMPTE OriginalPte, PageTablePte;
510 ULONG_PTR Protection;
511 PFN_NUMBER PageFrameIndex;
512 PMMPFN Pfn1, Pfn2;
513 BOOLEAN OriginalProtection, DirtyPage;
514
515 /* Must be called with an valid prototype PTE, with the PFN lock held */
516 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
517 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
518
519 /* Get the page */
520 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
521
522 /* Get the PFN entry and set it as a prototype PTE */
523 Pfn1 = MiGetPfnEntry(PageFrameIndex);
524 Pfn1->u3.e1.PrototypePte = 1;
525
526 /* Increment the share count for the page table */
527 // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
528 // This could be beause MiDeletePte is now being called from strange code in Rosmm
529 PageTablePte = MiAddressToPte(PointerPte);
530 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
531 //Pfn2->u2.ShareCount++;
532
533 /* Check where we should be getting the protection information from */
534 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
535 {
536 /* Get the protection from the PTE, there's no real Proto PTE data */
537 Protection = PointerPte->u.Soft.Protection;
538
539 /* Remember that we did not use the proto protection */
540 OriginalProtection = FALSE;
541 }
542 else
543 {
544 /* Get the protection from the original PTE link */
545 OriginalPte = &Pfn1->OriginalPte;
546 Protection = OriginalPte->u.Soft.Protection;
547
548 /* Remember that we used the original protection */
549 OriginalProtection = TRUE;
550
551 /* Check if this was a write on a read only proto */
552 if ((StoreInstruction) && !(Protection & MM_READWRITE))
553 {
554 /* Clear the flag */
555 StoreInstruction = 0;
556 }
557 }
558
559 /* Check if this was a write on a non-COW page */
560 DirtyPage = FALSE;
561 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
562 {
563 /* Then the page should be marked dirty */
564 DirtyPage = TRUE;
565
566 /* ReactOS check */
567 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
568 }
569
570 /* Did we get a locked incoming PFN? */
571 if (*LockedProtoPfn)
572 {
573 /* Drop a reference */
574 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
575 MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
576 *LockedProtoPfn = NULL;
577 }
578
579 /* Release the PFN lock */
580 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
581
582 /* Remove caching bits */
583 Protection &= ~(MM_NOCACHE | MM_NOACCESS);
584
585 /* Setup caching */
586 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
587 {
588 /* Write combining, no caching */
589 MI_PAGE_DISABLE_CACHE(&TempPte);
590 MI_PAGE_WRITE_COMBINED(&TempPte);
591 }
592 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
593 {
594 /* Write through, no caching */
595 MI_PAGE_DISABLE_CACHE(&TempPte);
596 MI_PAGE_WRITE_THROUGH(&TempPte);
597 }
598
599 /* Check if this is a kernel or user address */
600 if (Address < MmSystemRangeStart)
601 {
602 /* Build the user PTE */
603 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
604 }
605 else
606 {
607 /* Build the kernel PTE */
608 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
609 }
610
611 /* Set the dirty flag if needed */
612 if (DirtyPage) TempPte.u.Hard.Dirty = TRUE;
613
614 /* Write the PTE */
615 MI_WRITE_VALID_PTE(PointerPte, TempPte);
616
617 /* Reset the protection if needed */
618 if (OriginalProtection) Protection = MM_ZERO_ACCESS;
619
620 /* Return success */
621 ASSERT(PointerPte == MiAddressToPte(Address));
622 return STATUS_SUCCESS;
623 }
624
625 NTSTATUS
626 NTAPI
627 MiResolveTransitionFault(IN PVOID FaultingAddress,
628 IN PMMPTE PointerPte,
629 IN PEPROCESS CurrentProcess,
630 IN KIRQL OldIrql,
631 OUT PVOID *InPageBlock)
632 {
633 PFN_NUMBER PageFrameIndex;
634 PMMPFN Pfn1;
635 MMPTE TempPte;
636 PMMPTE PointerToPteForProtoPage;
637 DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
638
639 /* Windowss does this check */
640 ASSERT(*InPageBlock == NULL);
641
642 /* ARM3 doesn't support this path */
643 ASSERT(OldIrql != MM_NOIRQL);
644
645 /* Capture the PTE and make sure it's in transition format */
646 TempPte = *PointerPte;
647 ASSERT((TempPte.u.Soft.Valid == 0) &&
648 (TempPte.u.Soft.Prototype == 0) &&
649 (TempPte.u.Soft.Transition == 1));
650
651 /* Get the PFN and the PFN entry */
652 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
653 DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
654 Pfn1 = MiGetPfnEntry(PageFrameIndex);
655
656 /* One more transition fault! */
657 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
658
659 /* This is from ARM3 -- Windows normally handles this here */
660 ASSERT(Pfn1->u4.InPageError == 0);
661
662 /* Not supported in ARM3 */
663 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
664
665 /* Windows checks there's some free pages and this isn't an in-page error */
666 ASSERT(MmAvailablePages >= 0);
667 ASSERT(Pfn1->u4.InPageError == 0);
668
669 /* ReactOS checks for this */
670 ASSERT(MmAvailablePages > 32);
671
672 /* Was this a transition page in the valid list, or free/zero list? */
673 if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
674 {
675 /* All Windows does here is a bunch of sanity checks */
676 DPRINT1("Transition in active list\n");
677 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
678 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
679 ASSERT(Pfn1->u2.ShareCount != 0);
680 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
681 }
682 else
683 {
684 /* Otherwise, the page is removed from its list */
685 DPRINT1("Transition page in free/zero list\n");
686 MiUnlinkPageFromList(Pfn1);
687 MiReferenceUnusedPageAndBumpLockCount(Pfn1);
688 }
689
690 /* At this point, there should no longer be any in-page errors */
691 ASSERT(Pfn1->u4.InPageError == 0);
692
693 /* Check if this was a PFN with no more share references */
694 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
695
696 /* Bump the share count and make the page valid */
697 Pfn1->u2.ShareCount++;
698 Pfn1->u3.e1.PageLocation = ActiveAndValid;
699
700 /* Prototype PTEs are in paged pool, which itself might be in transition */
701 if (FaultingAddress >= MmSystemRangeStart)
702 {
703 /* Check if this is a paged pool PTE in transition state */
704 PointerToPteForProtoPage = MiAddressToPte(PointerPte);
705 TempPte = *PointerToPteForProtoPage;
706 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
707 {
708 /* This isn't yet supported */
709 DPRINT1("Double transition fault not yet supported\n");
710 ASSERT(FALSE);
711 }
712 }
713
714 /* Build the transition PTE -- maybe a macro? */
715 ASSERT(PointerPte->u.Hard.Valid == 0);
716 ASSERT(PointerPte->u.Trans.Prototype == 0);
717 ASSERT(PointerPte->u.Trans.Transition == 1);
718 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
719 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
720 MiDetermineUserGlobalPteMask(PointerPte);
721
722 /* Is the PTE writeable? */
723 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
724 (TempPte.u.Hard.CopyOnWrite == 0))
725 {
726 /* Make it dirty */
727 TempPte.u.Hard.Dirty = TRUE;
728 }
729 else
730 {
731 /* Make it clean */
732 TempPte.u.Hard.Dirty = FALSE;
733 }
734
735 /* Write the valid PTE */
736 MI_WRITE_VALID_PTE(PointerPte, TempPte);
737
738 /* Return success */
739 return STATUS_PAGE_FAULT_TRANSITION;
740 }
741
742 NTSTATUS
743 NTAPI
744 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
745 IN PVOID Address,
746 IN PMMPTE PointerPte,
747 IN PMMPTE PointerProtoPte,
748 IN OUT PMMPFN *OutPfn,
749 OUT PVOID *PageFileData,
750 OUT PMMPTE PteValue,
751 IN PEPROCESS Process,
752 IN KIRQL OldIrql,
753 IN PVOID TrapInformation)
754 {
755 MMPTE TempPte, PteContents;
756 PMMPFN Pfn1;
757 PFN_NUMBER PageFrameIndex;
758 NTSTATUS Status;
759 PVOID InPageBlock = NULL;
760
761 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
762 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
763 ASSERT(PointerPte->u.Hard.Valid == 0);
764 ASSERT(PointerPte->u.Soft.Prototype == 1);
765
766 /* Read the prototype PTE and check if it's valid */
767 TempPte = *PointerProtoPte;
768 if (TempPte.u.Hard.Valid == 1)
769 {
770 /* One more user of this mapped page */
771 PageFrameIndex = PFN_FROM_PTE(&TempPte);
772 Pfn1 = MiGetPfnEntry(PageFrameIndex);
773 Pfn1->u2.ShareCount++;
774
775 /* Call it a transition */
776 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
777
778 /* Complete the prototype PTE fault -- this will release the PFN lock */
779 return MiCompleteProtoPteFault(StoreInstruction,
780 Address,
781 PointerPte,
782 PointerProtoPte,
783 OldIrql,
784 OutPfn);
785 }
786
787 /* Make sure there's some protection mask */
788 if (TempPte.u.Long == 0)
789 {
790 /* Release the lock */
791 DPRINT1("Access on reserved section?\n");
792 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
793 return STATUS_ACCESS_VIOLATION;
794 }
795
796 /* Check for access rights on the PTE proper */
797 PteContents = *PointerPte;
798 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
799 {
800 if (!PteContents.u.Proto.ReadOnly)
801 {
802 /* FIXME: CHECK FOR ACCESS */
803
804 /* Check for copy on write page */
805 if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
806 {
807 /* Not yet supported */
808 ASSERT(FALSE);
809 }
810 }
811 }
812 else
813 {
814 /* Check for copy on write page */
815 if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
816 {
817 /* Not yet supported */
818 ASSERT(FALSE);
819 }
820 }
821
822 /* Check for clone PTEs */
823 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
824
825 /* We don't support mapped files yet */
826 ASSERT(TempPte.u.Soft.Prototype == 0);
827
828 /* We might however have transition PTEs */
829 if (TempPte.u.Soft.Transition == 1)
830 {
831 /* Resolve the transition fault */
832 ASSERT(OldIrql != MM_NOIRQL);
833 Status = MiResolveTransitionFault(Address,
834 PointerProtoPte,
835 Process,
836 OldIrql,
837 &InPageBlock);
838 ASSERT(NT_SUCCESS(Status));
839 }
840 else
841 {
842 /* We also don't support paged out pages */
843 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
844
845 /* Resolve the demand zero fault */
846 Status = MiResolveDemandZeroFault(Address,
847 PointerProtoPte,
848 Process,
849 OldIrql);
850 ASSERT(NT_SUCCESS(Status));
851 }
852
853 /* Complete the prototype PTE fault -- this will release the PFN lock */
854 ASSERT(PointerPte->u.Hard.Valid == 0);
855 return MiCompleteProtoPteFault(StoreInstruction,
856 Address,
857 PointerPte,
858 PointerProtoPte,
859 OldIrql,
860 OutPfn);
861 }
862
863 NTSTATUS
864 NTAPI
865 MiDispatchFault(IN BOOLEAN StoreInstruction,
866 IN PVOID Address,
867 IN PMMPTE PointerPte,
868 IN PMMPTE PointerProtoPte,
869 IN BOOLEAN Recursive,
870 IN PEPROCESS Process,
871 IN PVOID TrapInformation,
872 IN PMMVAD Vad)
873 {
874 MMPTE TempPte;
875 KIRQL OldIrql, LockIrql;
876 NTSTATUS Status;
877 PMMPTE SuperProtoPte;
878 PMMPFN Pfn1, OutPfn = NULL;
879 PFN_NUMBER PageFrameIndex, PteCount, ProcessedPtes;
880 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
881 Address,
882 Process);
883
884 /* Make sure the addresses are ok */
885 ASSERT(PointerPte == MiAddressToPte(Address));
886
887 //
888 // Make sure APCs are off and we're not at dispatch
889 //
890 OldIrql = KeGetCurrentIrql();
891 ASSERT(OldIrql <= APC_LEVEL);
892 ASSERT(KeAreAllApcsDisabled() == TRUE);
893
894 //
895 // Grab a copy of the PTE
896 //
897 TempPte = *PointerPte;
898
899 /* Do we have a prototype PTE? */
900 if (PointerProtoPte)
901 {
902 /* This should never happen */
903 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
904
905 /* Check if this is a kernel-mode address */
906 SuperProtoPte = MiAddressToPte(PointerProtoPte);
907 if (Address >= MmSystemRangeStart)
908 {
909 /* Lock the PFN database */
910 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
911
912 /* Has the PTE been made valid yet? */
913 if (!SuperProtoPte->u.Hard.Valid)
914 {
915 ASSERT(FALSE);
916 }
917 else if (PointerPte->u.Hard.Valid == 1)
918 {
919 ASSERT(FALSE);
920 }
921
922 /* Resolve the fault -- this will release the PFN lock */
923 Status = MiResolveProtoPteFault(StoreInstruction,
924 Address,
925 PointerPte,
926 PointerProtoPte,
927 &OutPfn,
928 NULL,
929 NULL,
930 Process,
931 LockIrql,
932 TrapInformation);
933 ASSERT(Status == STATUS_SUCCESS);
934
935 /* Complete this as a transition fault */
936 ASSERT(OldIrql == KeGetCurrentIrql());
937 ASSERT(OldIrql <= APC_LEVEL);
938 ASSERT(KeAreAllApcsDisabled() == TRUE);
939 return Status;
940 }
941 else
942 {
943 /* We only handle the lookup path */
944 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
945
946 /* Is there a non-image VAD? */
947 if ((Vad) &&
948 (Vad->u.VadFlags.VadType != VadImageMap) &&
949 !(Vad->u2.VadFlags2.ExtendableFile))
950 {
951 /* One day, ReactOS will cluster faults */
952 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
953 DPRINT("Should cluster fault, but won't\n");
954 }
955
956 /* Only one PTE to handle for now */
957 PteCount = 1;
958 ProcessedPtes = 0;
959
960 /* Lock the PFN database */
961 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
962
963 /* We only handle the valid path */
964 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
965
966 /* Capture the PTE */
967 TempPte = *PointerProtoPte;
968
969 /* Loop to handle future case of clustered faults */
970 while (TRUE)
971 {
972 /* For our current usage, this should be true */
973 if (TempPte.u.Hard.Valid == 1)
974 {
975 /* Bump the share count on the PTE */
976 PageFrameIndex = PFN_FROM_PTE(&TempPte);
977 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
978 Pfn1->u2.ShareCount++;
979 }
980 else if ((TempPte.u.Soft.Prototype == 0) &&
981 (TempPte.u.Soft.Transition == 1))
982 {
983 /* No standby support yet */
984 ASSERT(FALSE);
985 }
986 else
987 {
988 /* Page is invalid, get out of the loop */
989 break;
990 }
991
992 /* One more done, was it the last? */
993 if (++ProcessedPtes == PteCount)
994 {
995 /* Complete the fault */
996 MiCompleteProtoPteFault(StoreInstruction,
997 Address,
998 PointerPte,
999 PointerProtoPte,
1000 LockIrql,
1001 &OutPfn);
1002
1003 /* THIS RELEASES THE PFN LOCK! */
1004 break;
1005 }
1006
1007 /* No clustered faults yet */
1008 ASSERT(FALSE);
1009 }
1010
1011 /* Did we resolve the fault? */
1012 if (ProcessedPtes)
1013 {
1014 /* Bump the transition count */
1015 InterlockedExchangeAdd(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1016 ProcessedPtes--;
1017
1018 /* Loop all the processing we did */
1019 ASSERT(ProcessedPtes == 0);
1020
1021 /* Complete this as a transition fault */
1022 ASSERT(OldIrql == KeGetCurrentIrql());
1023 ASSERT(OldIrql <= APC_LEVEL);
1024 ASSERT(KeAreAllApcsDisabled() == TRUE);
1025 return STATUS_PAGE_FAULT_TRANSITION;
1026 }
1027
1028 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1029 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1030 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1031 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1032 ASSERT(PointerPte->u.Hard.Valid == 0);
1033
1034 /* Resolve the fault -- this will release the PFN lock */
1035 Status = MiResolveProtoPteFault(StoreInstruction,
1036 Address,
1037 PointerPte,
1038 PointerProtoPte,
1039 &OutPfn,
1040 NULL,
1041 NULL,
1042 Process,
1043 LockIrql,
1044 TrapInformation);
1045 ASSERT(Status == STATUS_SUCCESS);
1046
1047 /* Did the routine clean out the PFN or should we? */
1048 if (OutPfn)
1049 {
1050 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1051 ASSERT(PointerProtoPte != NULL);
1052 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1053
1054 /* Dereference the locked PFN */
1055 MiDereferencePfnAndDropLockCount(OutPfn);
1056 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1057
1058 /* And now release the lock */
1059 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1060 }
1061
1062 /* Complete this as a transition fault */
1063 ASSERT(OldIrql == KeGetCurrentIrql());
1064 ASSERT(OldIrql <= APC_LEVEL);
1065 ASSERT(KeAreAllApcsDisabled() == TRUE);
1066 return Status;
1067 }
1068 }
1069
1070 //
1071 // The PTE must be invalid but not completely empty. It must also not be a
1072 // prototype PTE as that scenario should've been handled above. These are
1073 // all Windows checks
1074 //
1075 ASSERT(TempPte.u.Hard.Valid == 0);
1076 ASSERT(TempPte.u.Soft.Prototype == 0);
1077 ASSERT(TempPte.u.Long != 0);
1078
1079 //
1080 // No transition or page file software PTEs in ARM3 yet, so this must be a
1081 // demand zero page. These are all ReactOS checks
1082 //
1083 ASSERT(TempPte.u.Soft.Transition == 0);
1084 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1085
1086 //
1087 // If we got this far, the PTE can only be a demand zero PTE, which is what
1088 // we want. Go handle it!
1089 //
1090 Status = MiResolveDemandZeroFault(Address,
1091 PointerPte,
1092 Process,
1093 MM_NOIRQL);
1094 ASSERT(KeAreAllApcsDisabled() == TRUE);
1095 if (NT_SUCCESS(Status))
1096 {
1097 //
1098 // Make sure we're returning in a sane state and pass the status down
1099 //
1100 ASSERT(OldIrql == KeGetCurrentIrql());
1101 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1102 return Status;
1103 }
1104
1105 //
1106 // Generate an access fault
1107 //
1108 return STATUS_ACCESS_VIOLATION;
1109 }
1110
1111 NTSTATUS
1112 NTAPI
1113 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1114 IN PVOID Address,
1115 IN KPROCESSOR_MODE Mode,
1116 IN PVOID TrapInformation)
1117 {
1118 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1119 PMMPTE ProtoPte = NULL;
1120 PMMPTE PointerPte = MiAddressToPte(Address);
1121 PMMPDE PointerPde = MiAddressToPde(Address);
1122 #if (_MI_PAGING_LEVELS >= 3)
1123 PMMPDE PointerPpe = MiAddressToPpe(Address);
1124 #if (_MI_PAGING_LEVELS == 4)
1125 PMMPDE PointerPxe = MiAddressToPxe(Address);
1126 #endif
1127 #endif
1128 MMPTE TempPte;
1129 PETHREAD CurrentThread;
1130 PEPROCESS CurrentProcess;
1131 NTSTATUS Status;
1132 PMMSUPPORT WorkingSet;
1133 ULONG ProtectionCode;
1134 PMMVAD Vad;
1135 PFN_NUMBER PageFrameIndex;
1136 ULONG Color;
1137 BOOLEAN IsSessionAddress;
1138 PMMPFN Pfn1;
1139 DPRINT("ARM3 FAULT AT: %p\n", Address);
1140
1141 /* Check for page fault on high IRQL */
1142 if (OldIrql > APC_LEVEL)
1143 {
1144 #if (_MI_PAGING_LEVELS < 3)
1145 /* Could be a page table for paged pool, which we'll allow */
1146 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1147 MiCheckPdeForPagedPool(Address);
1148 #endif
1149 /* Check if any of the top-level pages are invalid */
1150 if (
1151 #if (_MI_PAGING_LEVELS == 4)
1152 (PointerPxe->u.Hard.Valid == 0) ||
1153 #endif
1154 #if (_MI_PAGING_LEVELS >= 3)
1155 (PointerPpe->u.Hard.Valid == 0) ||
1156 #endif
1157 (PointerPde->u.Hard.Valid == 0))
1158 {
1159 /* This fault is not valid, printf out some debugging help */
1160 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1161 Address,
1162 OldIrql);
1163 if (TrapInformation)
1164 {
1165 PKTRAP_FRAME TrapFrame = TrapInformation;
1166 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1167 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1168 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1169 }
1170
1171 /* Tell the trap handler to fail */
1172 return STATUS_IN_PAGE_ERROR | 0x10000000;
1173 }
1174
1175 /* Not yet implemented in ReactOS */
1176 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1177 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1178
1179 /* Check if this was a write */
1180 if (StoreInstruction)
1181 {
1182 /* Was it to a read-only page? */
1183 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1184 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1185 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1186 {
1187 /* Crash with distinguished bugcheck code */
1188 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1189 (ULONG_PTR)Address,
1190 PointerPte->u.Long,
1191 (ULONG_PTR)TrapInformation,
1192 10);
1193 }
1194 }
1195
1196 /* Nothing is actually wrong */
1197 DPRINT1("Fault at IRQL1 is ok\n");
1198 return STATUS_SUCCESS;
1199 }
1200
1201 /* Check for kernel fault address */
1202 if (Address >= MmSystemRangeStart)
1203 {
1204 /* Bail out, if the fault came from user mode */
1205 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1206
1207 #if (_MI_PAGING_LEVELS == 4)
1208 /* AMD64 system, check if PXE is invalid */
1209 if (PointerPxe->u.Hard.Valid == 0)
1210 {
1211 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1212 (ULONG_PTR)Address,
1213 StoreInstruction,
1214 (ULONG_PTR)TrapInformation,
1215 7);
1216 }
1217 #endif
1218 #if (_MI_PAGING_LEVELS == 4)
1219 /* PAE/AMD64 system, check if PPE is invalid */
1220 if (PointerPpe->u.Hard.Valid == 0)
1221 {
1222 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1223 (ULONG_PTR)Address,
1224 StoreInstruction,
1225 (ULONG_PTR)TrapInformation,
1226 5);
1227 }
1228 #endif
1229 #if (_MI_PAGING_LEVELS == 2)
1230 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1231 MiCheckPdeForPagedPool(Address);
1232 #endif
1233
1234 /* Check if the PDE is invalid */
1235 if (PointerPde->u.Hard.Valid == 0)
1236 {
1237 /* PDE (still) not valid, kill the system */
1238 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1239 (ULONG_PTR)Address,
1240 StoreInstruction,
1241 (ULONG_PTR)TrapInformation,
1242 2);
1243 }
1244
1245 /* Not handling session faults yet */
1246 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1247
1248 /* The PDE is valid, so read the PTE */
1249 TempPte = *PointerPte;
1250 if (TempPte.u.Hard.Valid == 1)
1251 {
1252 /* Check if this was system space or session space */
1253 if (!IsSessionAddress)
1254 {
1255 /* Check if the PTE is still valid under PFN lock */
1256 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1257 TempPte = *PointerPte;
1258 if (TempPte.u.Hard.Valid)
1259 {
1260 /* Check if this was a write */
1261 if (StoreInstruction)
1262 {
1263 /* Was it to a read-only page? */
1264 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1265 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1266 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1267 {
1268 /* Crash with distinguished bugcheck code */
1269 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1270 (ULONG_PTR)Address,
1271 PointerPte->u.Long,
1272 (ULONG_PTR)TrapInformation,
1273 11);
1274 }
1275 }
1276 }
1277
1278 /* Release PFN lock and return all good */
1279 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1280 return STATUS_SUCCESS;
1281 }
1282 }
1283
1284 /* Check if this was a session PTE that needs to remap the session PDE */
1285 if (MI_IS_SESSION_PTE(Address))
1286 {
1287 /* Do the remapping */
1288 Status = MiCheckPdeForSessionSpace(Address);
1289 if (!NT_SUCCESS(Status))
1290 {
1291 /* It failed, this address is invalid */
1292 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1293 (ULONG_PTR)Address,
1294 StoreInstruction,
1295 (ULONG_PTR)TrapInformation,
1296 6);
1297 }
1298 }
1299
1300 /* Check for a fault on the page table or hyperspace */
1301 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1302 {
1303 #if (_MI_PAGING_LEVELS < 3)
1304 /* Windows does this check but I don't understand why -- it's done above! */
1305 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1306 #endif
1307 /* Handle this as a user mode fault */
1308 goto UserFault;
1309 }
1310
1311 /* Get the current thread */
1312 CurrentThread = PsGetCurrentThread();
1313
1314 /* What kind of address is this */
1315 if (!IsSessionAddress)
1316 {
1317 /* Use the system working set */
1318 WorkingSet = &MmSystemCacheWs;
1319 CurrentProcess = NULL;
1320
1321 /* Make sure we don't have a recursive working set lock */
1322 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1323 (CurrentThread->OwnsProcessWorkingSetShared) ||
1324 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1325 (CurrentThread->OwnsSystemWorkingSetShared) ||
1326 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1327 (CurrentThread->OwnsSessionWorkingSetShared))
1328 {
1329 /* Fail */
1330 return STATUS_IN_PAGE_ERROR | 0x10000000;
1331 }
1332 }
1333 else
1334 {
1335 /* Use the session process and working set */
1336 CurrentProcess = HYDRA_PROCESS;
1337 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1338
1339 /* Make sure we don't have a recursive working set lock */
1340 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1341 (CurrentThread->OwnsSessionWorkingSetShared))
1342 {
1343 /* Fail */
1344 return STATUS_IN_PAGE_ERROR | 0x10000000;
1345 }
1346 }
1347
1348 /* Acquire the working set lock */
1349 KeRaiseIrql(APC_LEVEL, &LockIrql);
1350 MiLockWorkingSet(CurrentThread, WorkingSet);
1351
1352 /* Re-read PTE now that we own the lock */
1353 TempPte = *PointerPte;
1354 if (TempPte.u.Hard.Valid == 1)
1355 {
1356 /* Check if this was a write */
1357 if (StoreInstruction)
1358 {
1359 /* Was it to a read-only page that is not copy on write? */
1360 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1361 if (!(TempPte.u.Long & PTE_READWRITE) &&
1362 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1363 !(TempPte.u.Hard.CopyOnWrite))
1364 {
1365 /* Case not yet handled */
1366 ASSERT(!IsSessionAddress);
1367
1368 /* Crash with distinguished bugcheck code */
1369 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1370 (ULONG_PTR)Address,
1371 TempPte.u.Long,
1372 (ULONG_PTR)TrapInformation,
1373 12);
1374 }
1375 }
1376
1377 /* Check for read-only write in session space */
1378 if ((IsSessionAddress) &&
1379 (StoreInstruction) &&
1380 !(TempPte.u.Hard.Write))
1381 {
1382 /* Sanity check */
1383 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1384
1385 /* Was this COW? */
1386 if (TempPte.u.Hard.CopyOnWrite == 0)
1387 {
1388 /* Then this is not allowed */
1389 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1390 (ULONG_PTR)Address,
1391 (ULONG_PTR)TempPte.u.Long,
1392 (ULONG_PTR)TrapInformation,
1393 13);
1394 }
1395
1396 /* Otherwise, handle COW */
1397 ASSERT(FALSE);
1398 }
1399
1400 /* Release the working set */
1401 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1402 KeLowerIrql(LockIrql);
1403
1404 /* Otherwise, the PDE was probably invalid, and all is good now */
1405 return STATUS_SUCCESS;
1406 }
1407
1408 /* Check one kind of prototype PTE */
1409 if (TempPte.u.Soft.Prototype)
1410 {
1411 /* Make sure protected pool is on, and that this is a pool address */
1412 if ((MmProtectFreedNonPagedPool) &&
1413 (((Address >= MmNonPagedPoolStart) &&
1414 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1415 MmSizeOfNonPagedPoolInBytes))) ||
1416 ((Address >= MmNonPagedPoolExpansionStart) &&
1417 (Address < MmNonPagedPoolEnd))))
1418 {
1419 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1420 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1421 (ULONG_PTR)Address,
1422 StoreInstruction,
1423 Mode,
1424 4);
1425 }
1426
1427 /* Get the prototype PTE! */
1428 ProtoPte = MiProtoPteToPte(&TempPte);
1429
1430 /* Do we need to locate the prototype PTE in session space? */
1431 if ((IsSessionAddress) &&
1432 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1433 {
1434 /* Yep, go find it as well as the VAD for it */
1435 ProtoPte = MiCheckVirtualAddress(Address,
1436 &ProtectionCode,
1437 &Vad);
1438 ASSERT(ProtoPte != NULL);
1439 }
1440 }
1441 else
1442 {
1443 /* We don't implement transition PTEs */
1444 ASSERT(TempPte.u.Soft.Transition == 0);
1445
1446 /* Check for no-access PTE */
1447 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1448 {
1449 /* Bugcheck the system! */
1450 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1451 (ULONG_PTR)Address,
1452 StoreInstruction,
1453 (ULONG_PTR)TrapInformation,
1454 1);
1455 }
1456 }
1457
1458 /* Check for demand page */
1459 if ((StoreInstruction) &&
1460 !(ProtoPte) &&
1461 !(IsSessionAddress) &&
1462 !(TempPte.u.Hard.Valid))
1463 {
1464 /* Get the protection code */
1465 ASSERT(TempPte.u.Soft.Transition == 0);
1466 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1467 {
1468 /* Bugcheck the system! */
1469 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1470 (ULONG_PTR)Address,
1471 TempPte.u.Long,
1472 (ULONG_PTR)TrapInformation,
1473 14);
1474 }
1475 }
1476
1477 /* Now do the real fault handling */
1478 Status = MiDispatchFault(StoreInstruction,
1479 Address,
1480 PointerPte,
1481 ProtoPte,
1482 FALSE,
1483 CurrentProcess,
1484 TrapInformation,
1485 NULL);
1486
1487 /* Release the working set */
1488 ASSERT(KeAreAllApcsDisabled() == TRUE);
1489 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1490 KeLowerIrql(LockIrql);
1491
1492 /* We are done! */
1493 DPRINT("Fault resolved with status: %lx\n", Status);
1494 return Status;
1495 }
1496
1497 /* This is a user fault */
1498 UserFault:
1499 CurrentThread = PsGetCurrentThread();
1500 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1501
1502 /* Lock the working set */
1503 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1504
1505 #if (_MI_PAGING_LEVELS == 4)
1506 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1507 // also this is missing the page count increment
1508 /* Check if the PXE is valid */
1509 if (PointerPxe->u.Hard.Valid == 0)
1510 {
1511 /* Right now, we only handle scenarios where the PXE is totally empty */
1512 ASSERT(PointerPxe->u.Long == 0);
1513 #if 0
1514 /* Resolve a demand zero fault */
1515 Status = MiResolveDemandZeroFault(PointerPpe,
1516 MM_READWRITE,
1517 CurrentProcess,
1518 MM_NOIRQL);
1519 #endif
1520 /* We should come back with a valid PXE */
1521 ASSERT(PointerPxe->u.Hard.Valid == 1);
1522 }
1523 #endif
1524
1525 #if (_MI_PAGING_LEVELS >= 3)
1526 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1527 // also this is missing the page count increment
1528 /* Check if the PPE is valid */
1529 if (PointerPpe->u.Hard.Valid == 0)
1530 {
1531 /* Right now, we only handle scenarios where the PPE is totally empty */
1532 ASSERT(PointerPpe->u.Long == 0);
1533 #if 0
1534 /* Resolve a demand zero fault */
1535 Status = MiResolveDemandZeroFault(PointerPde,
1536 MM_READWRITE,
1537 CurrentProcess,
1538 MM_NOIRQL);
1539 #endif
1540 /* We should come back with a valid PPE */
1541 ASSERT(PointerPpe->u.Hard.Valid == 1);
1542 }
1543 #endif
1544
1545 /* Check if the PDE is valid */
1546 if (PointerPde->u.Hard.Valid == 0)
1547 {
1548 /* Right now, we only handle scenarios where the PDE is totally empty */
1549 ASSERT(PointerPde->u.Long == 0);
1550
1551 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1552 #if MI_TRACE_PFNS
1553 UserPdeFault = TRUE;
1554 #endif
1555 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1556 if (ProtectionCode == MM_NOACCESS)
1557 {
1558 #if (_MI_PAGING_LEVELS == 2)
1559 /* Could be a page table for paged pool */
1560 MiCheckPdeForPagedPool(Address);
1561 #endif
1562 /* Has the code above changed anything -- is this now a valid PTE? */
1563 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1564
1565 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1566 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1567 return Status;
1568 }
1569
1570 /* Write a demand-zero PDE */
1571 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1572
1573 /* Dispatch the fault */
1574 Status = MiDispatchFault(TRUE,
1575 PointerPte,
1576 PointerPde,
1577 NULL,
1578 FALSE,
1579 PsGetCurrentProcess(),
1580 TrapInformation,
1581 NULL);
1582 #if MI_TRACE_PFNS
1583 UserPdeFault = FALSE;
1584 #endif
1585 /* We should come back with APCs enabled, and with a valid PDE */
1586 ASSERT(KeAreAllApcsDisabled() == TRUE);
1587 ASSERT(PointerPde->u.Hard.Valid == 1);
1588 }
1589 else
1590 {
1591 /* Not yet implemented in ReactOS */
1592 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1593 }
1594
1595 /* Now capture the PTE. Ignore virtual faults for now */
1596 TempPte = *PointerPte;
1597 ASSERT(TempPte.u.Hard.Valid == 0);
1598
1599 /* Quick check for demand-zero */
1600 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1601 {
1602 /* Resolve the fault */
1603 MiResolveDemandZeroFault(Address,
1604 PointerPte,
1605 CurrentProcess,
1606 MM_NOIRQL);
1607
1608 /* Return the status */
1609 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1610 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1611 }
1612
1613 /* Check for zero PTE */
1614 if (TempPte.u.Long == 0)
1615 {
1616 /* Check if this address range belongs to a valid allocation (VAD) */
1617 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1618 if (ProtectionCode == MM_NOACCESS)
1619 {
1620 #if (_MI_PAGING_LEVELS == 2)
1621 /* Could be a page table for paged pool */
1622 MiCheckPdeForPagedPool(Address);
1623 #endif
1624 /* Has the code above changed anything -- is this now a valid PTE? */
1625 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1626
1627 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1628 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1629 return Status;
1630 }
1631
1632 /* No guard page support yet */
1633 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1634
1635 /*
1636 * Check if this is a real user-mode address or actually a kernel-mode
1637 * page table for a user mode address
1638 */
1639 if (Address <= MM_HIGHEST_USER_ADDRESS)
1640 {
1641 /* Add an additional page table reference */
1642 MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
1643 ASSERT(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
1644 }
1645
1646 /* Did we get a prototype PTE back? */
1647 if (!ProtoPte)
1648 {
1649 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1650 if (PointerPde == MiAddressToPde(PTE_BASE))
1651 {
1652 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1653 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1654 }
1655 else
1656 {
1657 /* No, create a new PTE. First, write the protection */
1658 PointerPte->u.Soft.Protection = ProtectionCode;
1659 }
1660
1661 /* Lock the PFN database since we're going to grab a page */
1662 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1663
1664 /* Make sure we have enough pages */
1665 ASSERT(MmAvailablePages >= 32);
1666
1667 /* Try to get a zero page */
1668 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1669 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1670 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1671 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1672 if (!PageFrameIndex)
1673 {
1674 /* Grab a page out of there. Later we should grab a colored zero page */
1675 PageFrameIndex = MiRemoveAnyPage(Color);
1676 ASSERT(PageFrameIndex);
1677
1678 /* Release the lock since we need to do some zeroing */
1679 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1680
1681 /* Zero out the page, since it's for user-mode */
1682 MiZeroPfn(PageFrameIndex);
1683
1684 /* Grab the lock again so we can initialize the PFN entry */
1685 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1686 }
1687
1688 /* Initialize the PFN entry now */
1689 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1690
1691 /* And we're done with the lock */
1692 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1693
1694 /* Increment the count of pages in the process */
1695 CurrentProcess->NumberOfPrivatePages++;
1696
1697 /* One more demand-zero fault */
1698 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1699
1700 /* Fault on user PDE, or fault on user PTE? */
1701 if (PointerPte <= MiHighestUserPte)
1702 {
1703 /* User fault, build a user PTE */
1704 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1705 PointerPte,
1706 PointerPte->u.Soft.Protection,
1707 PageFrameIndex);
1708 }
1709 else
1710 {
1711 /* This is a user-mode PDE, create a kernel PTE for it */
1712 MI_MAKE_HARDWARE_PTE(&TempPte,
1713 PointerPte,
1714 PointerPte->u.Soft.Protection,
1715 PageFrameIndex);
1716 }
1717
1718 /* Write the dirty bit for writeable pages */
1719 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1720
1721 /* And now write down the PTE, making the address valid */
1722 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1723 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1724 ASSERT(Pfn1->u1.Event == NULL);
1725
1726 /* Demand zero */
1727 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1728 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1729 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1730 }
1731
1732 /* No guard page support yet */
1733 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1734 ASSERT(ProtectionCode != 0x100);
1735
1736 /* Write the prototype PTE */
1737 TempPte = PrototypePte;
1738 TempPte.u.Soft.Protection = ProtectionCode;
1739 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1740 }
1741 else
1742 {
1743 /* Get the protection code and check if this is a proto PTE */
1744 ProtectionCode = TempPte.u.Soft.Protection;
1745 DPRINT1("Code: %lx\n", ProtectionCode);
1746 if (TempPte.u.Soft.Prototype)
1747 {
1748 /* Do we need to go find the real PTE? */
1749 DPRINT1("Soft: %lx\n", TempPte.u.Soft.PageFileHigh);
1750 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1751 {
1752 /* Get the prototype pte and VAD for it */
1753 ProtoPte = MiCheckVirtualAddress(Address,
1754 &ProtectionCode,
1755 &Vad);
1756 DPRINT1("Address: %p ProtoP %p Code: %lx Vad: %p\n", Address, ProtoPte, ProtectionCode, Vad);
1757 if (!ProtoPte)
1758 {
1759 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1760 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1761 return STATUS_ACCESS_VIOLATION;
1762 }
1763 }
1764 else
1765 {
1766 /* Get the prototype PTE! */
1767 ProtoPte = MiProtoPteToPte(&TempPte);
1768
1769 /* Is it read-only */
1770 if (TempPte.u.Proto.ReadOnly)
1771 {
1772 /* Set read-only code */
1773 ProtectionCode = MM_READONLY;
1774 }
1775 else
1776 {
1777 /* Set unknown protection */
1778 ProtectionCode = 0x100;
1779 ASSERT(CurrentProcess->CloneRoot != NULL);
1780 }
1781 }
1782 }
1783 }
1784
1785 /* FIXME: Run MiAccessCheck */
1786
1787 /* Dispatch the fault */
1788 Status = MiDispatchFault(StoreInstruction,
1789 Address,
1790 PointerPte,
1791 ProtoPte,
1792 FALSE,
1793 CurrentProcess,
1794 TrapInformation,
1795 Vad);
1796
1797 /* Return the status */
1798 ASSERT(NT_SUCCESS(Status));
1799 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1800 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1801 return Status;
1802 }
1803
1804 NTSTATUS
1805 NTAPI
1806 MmGetExecuteOptions(IN PULONG ExecuteOptions)
1807 {
1808 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1809 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1810
1811 *ExecuteOptions = 0;
1812
1813 if (CurrentProcess->Flags.ExecuteDisable)
1814 {
1815 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
1816 }
1817
1818 if (CurrentProcess->Flags.ExecuteEnable)
1819 {
1820 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
1821 }
1822
1823 if (CurrentProcess->Flags.DisableThunkEmulation)
1824 {
1825 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
1826 }
1827
1828 if (CurrentProcess->Flags.Permanent)
1829 {
1830 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
1831 }
1832
1833 if (CurrentProcess->Flags.ExecuteDispatchEnable)
1834 {
1835 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
1836 }
1837
1838 if (CurrentProcess->Flags.ImageDispatchEnable)
1839 {
1840 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
1841 }
1842
1843 return STATUS_SUCCESS;
1844 }
1845
1846 NTSTATUS
1847 NTAPI
1848 MmSetExecuteOptions(IN ULONG ExecuteOptions)
1849 {
1850 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1851 KLOCK_QUEUE_HANDLE ProcessLock;
1852 NTSTATUS Status = STATUS_ACCESS_DENIED;
1853 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1854
1855 /* Only accept valid flags */
1856 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
1857 {
1858 /* Fail */
1859 DPRINT1("Invalid no-execute options\n");
1860 return STATUS_INVALID_PARAMETER;
1861 }
1862
1863 /* Change the NX state in the process lock */
1864 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
1865
1866 /* Don't change anything if the permanent flag was set */
1867 if (!CurrentProcess->Flags.Permanent)
1868 {
1869 /* Start by assuming it's not disabled */
1870 CurrentProcess->Flags.ExecuteDisable = FALSE;
1871
1872 /* Now process each flag and turn the equivalent bit on */
1873 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
1874 {
1875 CurrentProcess->Flags.ExecuteDisable = TRUE;
1876 }
1877 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
1878 {
1879 CurrentProcess->Flags.ExecuteEnable = TRUE;
1880 }
1881 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
1882 {
1883 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
1884 }
1885 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
1886 {
1887 CurrentProcess->Flags.Permanent = TRUE;
1888 }
1889 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
1890 {
1891 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1892 }
1893 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
1894 {
1895 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1896 }
1897
1898 /* These are turned on by default if no-execution is also eanbled */
1899 if (CurrentProcess->Flags.ExecuteEnable)
1900 {
1901 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1902 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1903 }
1904
1905 /* All good */
1906 Status = STATUS_SUCCESS;
1907 }
1908
1909 /* Release the lock and return status */
1910 KiReleaseProcessLock(&ProcessLock);
1911 return Status;
1912 }
1913
1914 /* EOF */