Sync with trunk.
[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 "../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 SessionAddress;
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 SessionAddress = MiPteToAddress(Address);
175 PointerPde = MiAddressToPte(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)SessionAddress - (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, SessionAddress);
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 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
533
534 /* Check where we should be getting the protection information from */
535 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
536 {
537 /* Get the protection from the PTE, there's no real Proto PTE data */
538 Protection = PointerPte->u.Soft.Protection;
539
540 /* Remember that we did not use the proto protection */
541 OriginalProtection = FALSE;
542 }
543 else
544 {
545 /* Get the protection from the original PTE link */
546 OriginalPte = &Pfn1->OriginalPte;
547 Protection = OriginalPte->u.Soft.Protection;
548
549 /* Remember that we used the original protection */
550 OriginalProtection = TRUE;
551
552 /* Check if this was a write on a read only proto */
553 if ((StoreInstruction) && !(Protection & MM_READWRITE))
554 {
555 /* Clear the flag */
556 StoreInstruction = 0;
557 }
558 }
559
560 /* Check if this was a write on a non-COW page */
561 DirtyPage = FALSE;
562 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
563 {
564 /* Then the page should be marked dirty */
565 DirtyPage = TRUE;
566
567 /* ReactOS check */
568 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
569 }
570
571 /* Did we get a locked incoming PFN? */
572 if (*LockedProtoPfn)
573 {
574 /* Drop a reference */
575 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
576 MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
577 *LockedProtoPfn = NULL;
578 }
579
580 /* Release the PFN lock */
581 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
582
583 /* Remove caching bits */
584 Protection &= ~(MM_NOCACHE | MM_NOACCESS);
585
586 /* Setup caching */
587 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
588 {
589 /* Write combining, no caching */
590 MI_PAGE_DISABLE_CACHE(&TempPte);
591 MI_PAGE_WRITE_COMBINED(&TempPte);
592 }
593 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
594 {
595 /* Write through, no caching */
596 MI_PAGE_DISABLE_CACHE(&TempPte);
597 MI_PAGE_WRITE_THROUGH(&TempPte);
598 }
599
600 /* Check if this is a kernel or user address */
601 if (Address < MmSystemRangeStart)
602 {
603 /* Build the user PTE */
604 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
605 }
606 else
607 {
608 /* Build the kernel PTE */
609 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
610 }
611
612 /* Set the dirty flag if needed */
613 if (DirtyPage) TempPte.u.Hard.Dirty = TRUE;
614
615 /* Write the PTE */
616 MI_WRITE_VALID_PTE(PointerPte, TempPte);
617
618 /* Reset the protection if needed */
619 if (OriginalProtection) Protection = MM_ZERO_ACCESS;
620
621 /* Return success */
622 ASSERT(PointerPte == MiAddressToPte(Address));
623 return STATUS_SUCCESS;
624 }
625
626 NTSTATUS
627 NTAPI
628 MiResolveTransitionFault(IN PVOID FaultingAddress,
629 IN PMMPTE PointerPte,
630 IN PEPROCESS CurrentProcess,
631 IN KIRQL OldIrql,
632 OUT PVOID *InPageBlock)
633 {
634 PFN_NUMBER PageFrameIndex;
635 PMMPFN Pfn1;
636 MMPTE TempPte;
637 PMMPTE PointerToPteForProtoPage;
638 DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
639
640 /* Windowss does this check */
641 ASSERT(*InPageBlock == NULL);
642
643 /* ARM3 doesn't support this path */
644 ASSERT(OldIrql != MM_NOIRQL);
645
646 /* Capture the PTE and make sure it's in transition format */
647 TempPte = *PointerPte;
648 ASSERT((TempPte.u.Soft.Valid == 0) &&
649 (TempPte.u.Soft.Prototype == 0) &&
650 (TempPte.u.Soft.Transition == 1));
651
652 /* Get the PFN and the PFN entry */
653 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
654 DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
655 Pfn1 = MiGetPfnEntry(PageFrameIndex);
656
657 /* One more transition fault! */
658 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
659
660 /* This is from ARM3 -- Windows normally handles this here */
661 ASSERT(Pfn1->u4.InPageError == 0);
662
663 /* Not supported in ARM3 */
664 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
665
666 /* Windows checks there's some free pages and this isn't an in-page error */
667 ASSERT(MmAvailablePages > 0);
668 ASSERT(Pfn1->u4.InPageError == 0);
669
670 /* ReactOS checks for this */
671 ASSERT(MmAvailablePages > 32);
672
673 /* Was this a transition page in the valid list, or free/zero list? */
674 if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
675 {
676 /* All Windows does here is a bunch of sanity checks */
677 DPRINT1("Transition in active list\n");
678 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
679 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
680 ASSERT(Pfn1->u2.ShareCount != 0);
681 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
682 }
683 else
684 {
685 /* Otherwise, the page is removed from its list */
686 DPRINT1("Transition page in free/zero list\n");
687 MiUnlinkPageFromList(Pfn1);
688 MiReferenceUnusedPageAndBumpLockCount(Pfn1);
689 }
690
691 /* At this point, there should no longer be any in-page errors */
692 ASSERT(Pfn1->u4.InPageError == 0);
693
694 /* Check if this was a PFN with no more share references */
695 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
696
697 /* Bump the share count and make the page valid */
698 Pfn1->u2.ShareCount++;
699 Pfn1->u3.e1.PageLocation = ActiveAndValid;
700
701 /* Prototype PTEs are in paged pool, which itself might be in transition */
702 if (FaultingAddress >= MmSystemRangeStart)
703 {
704 /* Check if this is a paged pool PTE in transition state */
705 PointerToPteForProtoPage = MiAddressToPte(PointerPte);
706 TempPte = *PointerToPteForProtoPage;
707 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
708 {
709 /* This isn't yet supported */
710 DPRINT1("Double transition fault not yet supported\n");
711 ASSERT(FALSE);
712 }
713 }
714
715 /* Build the transition PTE -- maybe a macro? */
716 ASSERT(PointerPte->u.Hard.Valid == 0);
717 ASSERT(PointerPte->u.Trans.Prototype == 0);
718 ASSERT(PointerPte->u.Trans.Transition == 1);
719 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
720 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
721 MiDetermineUserGlobalPteMask(PointerPte);
722
723 /* Is the PTE writeable? */
724 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
725 (TempPte.u.Hard.CopyOnWrite == 0))
726 {
727 /* Make it dirty */
728 TempPte.u.Hard.Dirty = TRUE;
729 }
730 else
731 {
732 /* Make it clean */
733 TempPte.u.Hard.Dirty = FALSE;
734 }
735
736 /* Write the valid PTE */
737 MI_WRITE_VALID_PTE(PointerPte, TempPte);
738
739 /* Return success */
740 return STATUS_PAGE_FAULT_TRANSITION;
741 }
742
743 NTSTATUS
744 NTAPI
745 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
746 IN PVOID Address,
747 IN PMMPTE PointerPte,
748 IN PMMPTE PointerProtoPte,
749 IN OUT PMMPFN *OutPfn,
750 OUT PVOID *PageFileData,
751 OUT PMMPTE PteValue,
752 IN PEPROCESS Process,
753 IN KIRQL OldIrql,
754 IN PVOID TrapInformation)
755 {
756 MMPTE TempPte, PteContents;
757 PMMPFN Pfn1;
758 PFN_NUMBER PageFrameIndex;
759 NTSTATUS Status;
760 PVOID InPageBlock = NULL;
761
762 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
763 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
764 ASSERT(PointerPte->u.Hard.Valid == 0);
765 ASSERT(PointerPte->u.Soft.Prototype == 1);
766
767 /* Read the prototype PTE and check if it's valid */
768 TempPte = *PointerProtoPte;
769 if (TempPte.u.Hard.Valid == 1)
770 {
771 /* One more user of this mapped page */
772 PageFrameIndex = PFN_FROM_PTE(&TempPte);
773 Pfn1 = MiGetPfnEntry(PageFrameIndex);
774 Pfn1->u2.ShareCount++;
775
776 /* Call it a transition */
777 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
778
779 /* Complete the prototype PTE fault -- this will release the PFN lock */
780 return MiCompleteProtoPteFault(StoreInstruction,
781 Address,
782 PointerPte,
783 PointerProtoPte,
784 OldIrql,
785 OutPfn);
786 }
787
788 /* Make sure there's some protection mask */
789 if (TempPte.u.Long == 0)
790 {
791 /* Release the lock */
792 DPRINT1("Access on reserved section?\n");
793 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
794 return STATUS_ACCESS_VIOLATION;
795 }
796
797 /* Check for access rights on the PTE proper */
798 PteContents = *PointerPte;
799 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
800 {
801 if (!PteContents.u.Proto.ReadOnly)
802 {
803 /* FIXME: CHECK FOR ACCESS */
804
805 /* Check for copy on write page */
806 if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
807 {
808 /* Not yet supported */
809 ASSERT(FALSE);
810 }
811 }
812 }
813 else
814 {
815 /* Check for copy on write page */
816 if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
817 {
818 /* Not yet supported */
819 ASSERT(FALSE);
820 }
821 }
822
823 /* Check for clone PTEs */
824 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
825
826 /* We don't support mapped files yet */
827 ASSERT(TempPte.u.Soft.Prototype == 0);
828
829 /* We might however have transition PTEs */
830 if (TempPte.u.Soft.Transition == 1)
831 {
832 /* Resolve the transition fault */
833 ASSERT(OldIrql != MM_NOIRQL);
834 Status = MiResolveTransitionFault(Address,
835 PointerProtoPte,
836 Process,
837 OldIrql,
838 &InPageBlock);
839 ASSERT(NT_SUCCESS(Status));
840 }
841 else
842 {
843 /* We also don't support paged out pages */
844 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
845
846 /* Resolve the demand zero fault */
847 Status = MiResolveDemandZeroFault(Address,
848 PointerProtoPte,
849 Process,
850 OldIrql);
851 ASSERT(NT_SUCCESS(Status));
852 }
853
854 /* Complete the prototype PTE fault -- this will release the PFN lock */
855 ASSERT(PointerPte->u.Hard.Valid == 0);
856 return MiCompleteProtoPteFault(StoreInstruction,
857 Address,
858 PointerPte,
859 PointerProtoPte,
860 OldIrql,
861 OutPfn);
862 }
863
864 NTSTATUS
865 NTAPI
866 MiDispatchFault(IN BOOLEAN StoreInstruction,
867 IN PVOID Address,
868 IN PMMPTE PointerPte,
869 IN PMMPTE PointerProtoPte,
870 IN BOOLEAN Recursive,
871 IN PEPROCESS Process,
872 IN PVOID TrapInformation,
873 IN PMMVAD Vad)
874 {
875 MMPTE TempPte;
876 KIRQL OldIrql, LockIrql;
877 NTSTATUS Status;
878 PMMPTE SuperProtoPte;
879 PMMPFN Pfn1, OutPfn = NULL;
880 PFN_NUMBER PageFrameIndex;
881 PFN_COUNT PteCount, ProcessedPtes;
882 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
883 Address,
884 Process);
885
886 /* Make sure the addresses are ok */
887 ASSERT(PointerPte == MiAddressToPte(Address));
888
889 //
890 // Make sure APCs are off and we're not at dispatch
891 //
892 OldIrql = KeGetCurrentIrql();
893 ASSERT(OldIrql <= APC_LEVEL);
894 ASSERT(KeAreAllApcsDisabled() == TRUE);
895
896 //
897 // Grab a copy of the PTE
898 //
899 TempPte = *PointerPte;
900
901 /* Do we have a prototype PTE? */
902 if (PointerProtoPte)
903 {
904 /* This should never happen */
905 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
906
907 /* Check if this is a kernel-mode address */
908 SuperProtoPte = MiAddressToPte(PointerProtoPte);
909 if (Address >= MmSystemRangeStart)
910 {
911 /* Lock the PFN database */
912 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
913
914 /* Has the PTE been made valid yet? */
915 if (!SuperProtoPte->u.Hard.Valid)
916 {
917 ASSERT(FALSE);
918 }
919 else if (PointerPte->u.Hard.Valid == 1)
920 {
921 ASSERT(FALSE);
922 }
923
924 /* Resolve the fault -- this will release the PFN lock */
925 Status = MiResolveProtoPteFault(StoreInstruction,
926 Address,
927 PointerPte,
928 PointerProtoPte,
929 &OutPfn,
930 NULL,
931 NULL,
932 Process,
933 LockIrql,
934 TrapInformation);
935 ASSERT(Status == STATUS_SUCCESS);
936
937 /* Complete this as a transition fault */
938 ASSERT(OldIrql == KeGetCurrentIrql());
939 ASSERT(OldIrql <= APC_LEVEL);
940 ASSERT(KeAreAllApcsDisabled() == TRUE);
941 return Status;
942 }
943 else
944 {
945 /* We only handle the lookup path */
946 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
947
948 /* Is there a non-image VAD? */
949 if ((Vad) &&
950 (Vad->u.VadFlags.VadType != VadImageMap) &&
951 !(Vad->u2.VadFlags2.ExtendableFile))
952 {
953 /* One day, ReactOS will cluster faults */
954 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
955 DPRINT("Should cluster fault, but won't\n");
956 }
957
958 /* Only one PTE to handle for now */
959 PteCount = 1;
960 ProcessedPtes = 0;
961
962 /* Lock the PFN database */
963 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
964
965 /* We only handle the valid path */
966 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
967
968 /* Capture the PTE */
969 TempPte = *PointerProtoPte;
970
971 /* Loop to handle future case of clustered faults */
972 while (TRUE)
973 {
974 /* For our current usage, this should be true */
975 if (TempPte.u.Hard.Valid == 1)
976 {
977 /* Bump the share count on the PTE */
978 PageFrameIndex = PFN_FROM_PTE(&TempPte);
979 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
980 Pfn1->u2.ShareCount++;
981 }
982 else if ((TempPte.u.Soft.Prototype == 0) &&
983 (TempPte.u.Soft.Transition == 1))
984 {
985 /* This is a standby page, bring it back from the cache */
986 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
987 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
988 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
989 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
990
991 /* Should not yet happen in ReactOS */
992 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
993 ASSERT(Pfn1->u4.InPageError == 0);
994
995 /* Get the page */
996 MiUnlinkPageFromList(Pfn1);
997
998 /* Bump its reference count */
999 ASSERT(Pfn1->u2.ShareCount == 0);
1000 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1001 Pfn1->u2.ShareCount++;
1002
1003 /* Make it valid again */
1004 /* This looks like another macro.... */
1005 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1006 ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1007 ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1008 ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1009 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1010 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1011 TempPte.u.Hard.Valid = 1;
1012 TempPte.u.Hard.Accessed = 1;
1013
1014 /* Is the PTE writeable? */
1015 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
1016 (TempPte.u.Hard.CopyOnWrite == 0))
1017 {
1018 /* Make it dirty */
1019 TempPte.u.Hard.Dirty = TRUE;
1020 }
1021 else
1022 {
1023 /* Make it clean */
1024 TempPte.u.Hard.Dirty = FALSE;
1025 }
1026
1027 /* Write the valid PTE */
1028 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1029 ASSERT(PointerPte->u.Hard.Valid == 0);
1030 }
1031 else
1032 {
1033 /* Page is invalid, get out of the loop */
1034 break;
1035 }
1036
1037 /* One more done, was it the last? */
1038 if (++ProcessedPtes == PteCount)
1039 {
1040 /* Complete the fault */
1041 MiCompleteProtoPteFault(StoreInstruction,
1042 Address,
1043 PointerPte,
1044 PointerProtoPte,
1045 LockIrql,
1046 &OutPfn);
1047
1048 /* THIS RELEASES THE PFN LOCK! */
1049 break;
1050 }
1051
1052 /* No clustered faults yet */
1053 ASSERT(FALSE);
1054 }
1055
1056 /* Did we resolve the fault? */
1057 if (ProcessedPtes)
1058 {
1059 /* Bump the transition count */
1060 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1061 ProcessedPtes--;
1062
1063 /* Loop all the processing we did */
1064 ASSERT(ProcessedPtes == 0);
1065
1066 /* Complete this as a transition fault */
1067 ASSERT(OldIrql == KeGetCurrentIrql());
1068 ASSERT(OldIrql <= APC_LEVEL);
1069 ASSERT(KeAreAllApcsDisabled() == TRUE);
1070 return STATUS_PAGE_FAULT_TRANSITION;
1071 }
1072
1073 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1074 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1075 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1076 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1077 ASSERT(PointerPte->u.Hard.Valid == 0);
1078
1079 /* Resolve the fault -- this will release the PFN lock */
1080 Status = MiResolveProtoPteFault(StoreInstruction,
1081 Address,
1082 PointerPte,
1083 PointerProtoPte,
1084 &OutPfn,
1085 NULL,
1086 NULL,
1087 Process,
1088 LockIrql,
1089 TrapInformation);
1090 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1091 //ASSERT(Status != STATUS_REFAULT);
1092 //ASSERT(Status != STATUS_PTE_CHANGED);
1093
1094 /* Did the routine clean out the PFN or should we? */
1095 if (OutPfn)
1096 {
1097 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1098 ASSERT(PointerProtoPte != NULL);
1099 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1100
1101 /* Dereference the locked PFN */
1102 MiDereferencePfnAndDropLockCount(OutPfn);
1103 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1104
1105 /* And now release the lock */
1106 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1107 }
1108
1109 /* Complete this as a transition fault */
1110 ASSERT(OldIrql == KeGetCurrentIrql());
1111 ASSERT(OldIrql <= APC_LEVEL);
1112 ASSERT(KeAreAllApcsDisabled() == TRUE);
1113 return Status;
1114 }
1115 }
1116
1117 //
1118 // The PTE must be invalid but not completely empty. It must also not be a
1119 // prototype PTE as that scenario should've been handled above. These are
1120 // all Windows checks
1121 //
1122 ASSERT(TempPte.u.Hard.Valid == 0);
1123 ASSERT(TempPte.u.Soft.Prototype == 0);
1124 ASSERT(TempPte.u.Long != 0);
1125
1126 //
1127 // No transition or page file software PTEs in ARM3 yet, so this must be a
1128 // demand zero page. These are all ReactOS checks
1129 //
1130 ASSERT(TempPte.u.Soft.Transition == 0);
1131 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1132
1133 //
1134 // If we got this far, the PTE can only be a demand zero PTE, which is what
1135 // we want. Go handle it!
1136 //
1137 Status = MiResolveDemandZeroFault(Address,
1138 PointerPte,
1139 Process,
1140 MM_NOIRQL);
1141 ASSERT(KeAreAllApcsDisabled() == TRUE);
1142 if (NT_SUCCESS(Status))
1143 {
1144 //
1145 // Make sure we're returning in a sane state and pass the status down
1146 //
1147 ASSERT(OldIrql == KeGetCurrentIrql());
1148 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1149 return Status;
1150 }
1151
1152 //
1153 // Generate an access fault
1154 //
1155 return STATUS_ACCESS_VIOLATION;
1156 }
1157
1158 NTSTATUS
1159 NTAPI
1160 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1161 IN PVOID Address,
1162 IN KPROCESSOR_MODE Mode,
1163 IN PVOID TrapInformation)
1164 {
1165 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1166 PMMPTE ProtoPte = NULL;
1167 PMMPTE PointerPte = MiAddressToPte(Address);
1168 PMMPDE PointerPde = MiAddressToPde(Address);
1169 #if (_MI_PAGING_LEVELS >= 3)
1170 PMMPDE PointerPpe = MiAddressToPpe(Address);
1171 #if (_MI_PAGING_LEVELS == 4)
1172 PMMPDE PointerPxe = MiAddressToPxe(Address);
1173 #endif
1174 #endif
1175 MMPTE TempPte;
1176 PETHREAD CurrentThread;
1177 PEPROCESS CurrentProcess;
1178 NTSTATUS Status;
1179 PMMSUPPORT WorkingSet;
1180 ULONG ProtectionCode;
1181 PMMVAD Vad;
1182 PFN_NUMBER PageFrameIndex;
1183 ULONG Color;
1184 BOOLEAN IsSessionAddress;
1185 PMMPFN Pfn1;
1186 DPRINT("ARM3 FAULT AT: %p\n", Address);
1187
1188 /* Check for page fault on high IRQL */
1189 if (OldIrql > APC_LEVEL)
1190 {
1191 #if (_MI_PAGING_LEVELS < 3)
1192 /* Could be a page table for paged pool, which we'll allow */
1193 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1194 MiCheckPdeForPagedPool(Address);
1195 #endif
1196 /* Check if any of the top-level pages are invalid */
1197 if (
1198 #if (_MI_PAGING_LEVELS == 4)
1199 (PointerPxe->u.Hard.Valid == 0) ||
1200 #endif
1201 #if (_MI_PAGING_LEVELS >= 3)
1202 (PointerPpe->u.Hard.Valid == 0) ||
1203 #endif
1204 (PointerPde->u.Hard.Valid == 0))
1205 {
1206 /* This fault is not valid, printf out some debugging help */
1207 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1208 Address,
1209 OldIrql);
1210 if (TrapInformation)
1211 {
1212 PKTRAP_FRAME TrapFrame = TrapInformation;
1213 #ifdef _M_IX86
1214 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1215 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1216 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1217 #elif defined(_M_AMD64)
1218 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1219 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1220 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1221 #endif
1222 }
1223
1224 /* Tell the trap handler to fail */
1225 return STATUS_IN_PAGE_ERROR | 0x10000000;
1226 }
1227
1228 /* Not yet implemented in ReactOS */
1229 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1230 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1231
1232 /* Check if this was a write */
1233 if (StoreInstruction)
1234 {
1235 /* Was it to a read-only page? */
1236 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1237 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1238 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1239 {
1240 /* Crash with distinguished bugcheck code */
1241 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1242 (ULONG_PTR)Address,
1243 PointerPte->u.Long,
1244 (ULONG_PTR)TrapInformation,
1245 10);
1246 }
1247 }
1248
1249 /* Nothing is actually wrong */
1250 DPRINT1("Fault at IRQL1 is ok\n");
1251 return STATUS_SUCCESS;
1252 }
1253
1254 /* Check for kernel fault address */
1255 if (Address >= MmSystemRangeStart)
1256 {
1257 /* Bail out, if the fault came from user mode */
1258 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1259
1260 #if (_MI_PAGING_LEVELS == 4)
1261 /* AMD64 system, check if PXE is invalid */
1262 if (PointerPxe->u.Hard.Valid == 0)
1263 {
1264 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1265 (ULONG_PTR)Address,
1266 StoreInstruction,
1267 (ULONG_PTR)TrapInformation,
1268 7);
1269 }
1270 #endif
1271 #if (_MI_PAGING_LEVELS == 4)
1272 /* PAE/AMD64 system, check if PPE is invalid */
1273 if (PointerPpe->u.Hard.Valid == 0)
1274 {
1275 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1276 (ULONG_PTR)Address,
1277 StoreInstruction,
1278 (ULONG_PTR)TrapInformation,
1279 5);
1280 }
1281 #endif
1282 #if (_MI_PAGING_LEVELS == 2)
1283 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1284 MiCheckPdeForPagedPool(Address);
1285 #endif
1286
1287 /* Check if the PDE is invalid */
1288 if (PointerPde->u.Hard.Valid == 0)
1289 {
1290 /* PDE (still) not valid, kill the system */
1291 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1292 (ULONG_PTR)Address,
1293 StoreInstruction,
1294 (ULONG_PTR)TrapInformation,
1295 2);
1296 }
1297
1298 /* Not handling session faults yet */
1299 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1300
1301 /* The PDE is valid, so read the PTE */
1302 TempPte = *PointerPte;
1303 if (TempPte.u.Hard.Valid == 1)
1304 {
1305 /* Check if this was system space or session space */
1306 if (!IsSessionAddress)
1307 {
1308 /* Check if the PTE is still valid under PFN lock */
1309 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1310 TempPte = *PointerPte;
1311 if (TempPte.u.Hard.Valid)
1312 {
1313 /* Check if this was a write */
1314 if (StoreInstruction)
1315 {
1316 /* Was it to a read-only page? */
1317 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1318 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1319 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1320 {
1321 /* Crash with distinguished bugcheck code */
1322 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1323 (ULONG_PTR)Address,
1324 PointerPte->u.Long,
1325 (ULONG_PTR)TrapInformation,
1326 11);
1327 }
1328 }
1329 }
1330
1331 /* Release PFN lock and return all good */
1332 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1333 return STATUS_SUCCESS;
1334 }
1335 }
1336
1337 /* Check if this was a session PTE that needs to remap the session PDE */
1338 if (MI_IS_SESSION_PTE(Address))
1339 {
1340 /* Do the remapping */
1341 Status = MiCheckPdeForSessionSpace(Address);
1342 if (!NT_SUCCESS(Status))
1343 {
1344 /* It failed, this address is invalid */
1345 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1346 (ULONG_PTR)Address,
1347 StoreInstruction,
1348 (ULONG_PTR)TrapInformation,
1349 6);
1350 }
1351 }
1352
1353 /* Check for a fault on the page table or hyperspace */
1354 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1355 {
1356 #if (_MI_PAGING_LEVELS < 3)
1357 /* Windows does this check but I don't understand why -- it's done above! */
1358 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1359 #endif
1360 /* Handle this as a user mode fault */
1361 goto UserFault;
1362 }
1363
1364 /* Get the current thread */
1365 CurrentThread = PsGetCurrentThread();
1366
1367 /* What kind of address is this */
1368 if (!IsSessionAddress)
1369 {
1370 /* Use the system working set */
1371 WorkingSet = &MmSystemCacheWs;
1372 CurrentProcess = NULL;
1373
1374 /* Make sure we don't have a recursive working set lock */
1375 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1376 (CurrentThread->OwnsProcessWorkingSetShared) ||
1377 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1378 (CurrentThread->OwnsSystemWorkingSetShared) ||
1379 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1380 (CurrentThread->OwnsSessionWorkingSetShared))
1381 {
1382 /* Fail */
1383 return STATUS_IN_PAGE_ERROR | 0x10000000;
1384 }
1385 }
1386 else
1387 {
1388 /* Use the session process and working set */
1389 CurrentProcess = HYDRA_PROCESS;
1390 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1391
1392 /* Make sure we don't have a recursive working set lock */
1393 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1394 (CurrentThread->OwnsSessionWorkingSetShared))
1395 {
1396 /* Fail */
1397 return STATUS_IN_PAGE_ERROR | 0x10000000;
1398 }
1399 }
1400
1401 /* Acquire the working set lock */
1402 KeRaiseIrql(APC_LEVEL, &LockIrql);
1403 MiLockWorkingSet(CurrentThread, WorkingSet);
1404
1405 /* Re-read PTE now that we own the lock */
1406 TempPte = *PointerPte;
1407 if (TempPte.u.Hard.Valid == 1)
1408 {
1409 /* Check if this was a write */
1410 if (StoreInstruction)
1411 {
1412 /* Was it to a read-only page that is not copy on write? */
1413 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1414 if (!(TempPte.u.Long & PTE_READWRITE) &&
1415 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1416 !(TempPte.u.Hard.CopyOnWrite))
1417 {
1418 /* Case not yet handled */
1419 ASSERT(!IsSessionAddress);
1420
1421 /* Crash with distinguished bugcheck code */
1422 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1423 (ULONG_PTR)Address,
1424 TempPte.u.Long,
1425 (ULONG_PTR)TrapInformation,
1426 12);
1427 }
1428 }
1429
1430 /* Check for read-only write in session space */
1431 if ((IsSessionAddress) &&
1432 (StoreInstruction) &&
1433 !(TempPte.u.Hard.Write))
1434 {
1435 /* Sanity check */
1436 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1437
1438 /* Was this COW? */
1439 if (TempPte.u.Hard.CopyOnWrite == 0)
1440 {
1441 /* Then this is not allowed */
1442 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1443 (ULONG_PTR)Address,
1444 (ULONG_PTR)TempPte.u.Long,
1445 (ULONG_PTR)TrapInformation,
1446 13);
1447 }
1448
1449 /* Otherwise, handle COW */
1450 ASSERT(FALSE);
1451 }
1452
1453 /* Release the working set */
1454 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1455 KeLowerIrql(LockIrql);
1456
1457 /* Otherwise, the PDE was probably invalid, and all is good now */
1458 return STATUS_SUCCESS;
1459 }
1460
1461 /* Check one kind of prototype PTE */
1462 if (TempPte.u.Soft.Prototype)
1463 {
1464 /* Make sure protected pool is on, and that this is a pool address */
1465 if ((MmProtectFreedNonPagedPool) &&
1466 (((Address >= MmNonPagedPoolStart) &&
1467 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1468 MmSizeOfNonPagedPoolInBytes))) ||
1469 ((Address >= MmNonPagedPoolExpansionStart) &&
1470 (Address < MmNonPagedPoolEnd))))
1471 {
1472 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1473 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1474 (ULONG_PTR)Address,
1475 StoreInstruction,
1476 Mode,
1477 4);
1478 }
1479
1480 /* Get the prototype PTE! */
1481 ProtoPte = MiProtoPteToPte(&TempPte);
1482
1483 /* Do we need to locate the prototype PTE in session space? */
1484 if ((IsSessionAddress) &&
1485 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1486 {
1487 /* Yep, go find it as well as the VAD for it */
1488 ProtoPte = MiCheckVirtualAddress(Address,
1489 &ProtectionCode,
1490 &Vad);
1491 ASSERT(ProtoPte != NULL);
1492 }
1493 }
1494 else
1495 {
1496 /* We don't implement transition PTEs */
1497 ASSERT(TempPte.u.Soft.Transition == 0);
1498
1499 /* Check for no-access PTE */
1500 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1501 {
1502 /* Bugcheck the system! */
1503 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1504 (ULONG_PTR)Address,
1505 StoreInstruction,
1506 (ULONG_PTR)TrapInformation,
1507 1);
1508 }
1509 }
1510
1511 /* Check for demand page */
1512 if ((StoreInstruction) &&
1513 !(ProtoPte) &&
1514 !(IsSessionAddress) &&
1515 !(TempPte.u.Hard.Valid))
1516 {
1517 /* Get the protection code */
1518 ASSERT(TempPte.u.Soft.Transition == 0);
1519 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1520 {
1521 /* Bugcheck the system! */
1522 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1523 (ULONG_PTR)Address,
1524 TempPte.u.Long,
1525 (ULONG_PTR)TrapInformation,
1526 14);
1527 }
1528 }
1529
1530 /* Now do the real fault handling */
1531 Status = MiDispatchFault(StoreInstruction,
1532 Address,
1533 PointerPte,
1534 ProtoPte,
1535 FALSE,
1536 CurrentProcess,
1537 TrapInformation,
1538 NULL);
1539
1540 /* Release the working set */
1541 ASSERT(KeAreAllApcsDisabled() == TRUE);
1542 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1543 KeLowerIrql(LockIrql);
1544
1545 /* We are done! */
1546 DPRINT("Fault resolved with status: %lx\n", Status);
1547 return Status;
1548 }
1549
1550 /* This is a user fault */
1551 UserFault:
1552 CurrentThread = PsGetCurrentThread();
1553 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1554
1555 /* Lock the working set */
1556 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1557
1558 #if (_MI_PAGING_LEVELS == 4)
1559 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1560 // also this is missing the page count increment
1561 /* Check if the PXE is valid */
1562 if (PointerPxe->u.Hard.Valid == 0)
1563 {
1564 /* Right now, we only handle scenarios where the PXE is totally empty */
1565 ASSERT(PointerPxe->u.Long == 0);
1566 #if 0
1567 /* Resolve a demand zero fault */
1568 Status = MiResolveDemandZeroFault(PointerPpe,
1569 MM_READWRITE,
1570 CurrentProcess,
1571 MM_NOIRQL);
1572 #endif
1573 /* We should come back with a valid PXE */
1574 ASSERT(PointerPxe->u.Hard.Valid == 1);
1575 }
1576 #endif
1577
1578 #if (_MI_PAGING_LEVELS >= 3)
1579 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1580 // also this is missing the page count increment
1581 /* Check if the PPE is valid */
1582 if (PointerPpe->u.Hard.Valid == 0)
1583 {
1584 /* Right now, we only handle scenarios where the PPE is totally empty */
1585 ASSERT(PointerPpe->u.Long == 0);
1586 #if 0
1587 /* Resolve a demand zero fault */
1588 Status = MiResolveDemandZeroFault(PointerPde,
1589 MM_READWRITE,
1590 CurrentProcess,
1591 MM_NOIRQL);
1592 #endif
1593 /* We should come back with a valid PPE */
1594 ASSERT(PointerPpe->u.Hard.Valid == 1);
1595 }
1596 #endif
1597
1598 /* Check if the PDE is valid */
1599 if (PointerPde->u.Hard.Valid == 0)
1600 {
1601 /* Right now, we only handle scenarios where the PDE is totally empty */
1602 ASSERT(PointerPde->u.Long == 0);
1603
1604 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1605 #if MI_TRACE_PFNS
1606 UserPdeFault = TRUE;
1607 #endif
1608 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1609 if (ProtectionCode == MM_NOACCESS)
1610 {
1611 #if (_MI_PAGING_LEVELS == 2)
1612 /* Could be a page table for paged pool */
1613 MiCheckPdeForPagedPool(Address);
1614 #endif
1615 /* Has the code above changed anything -- is this now a valid PTE? */
1616 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1617
1618 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1619 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1620 return Status;
1621 }
1622
1623 /* Write a demand-zero PDE */
1624 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1625
1626 /* Dispatch the fault */
1627 Status = MiDispatchFault(TRUE,
1628 PointerPte,
1629 PointerPde,
1630 NULL,
1631 FALSE,
1632 PsGetCurrentProcess(),
1633 TrapInformation,
1634 NULL);
1635 #if MI_TRACE_PFNS
1636 UserPdeFault = FALSE;
1637 #endif
1638 /* We should come back with APCs enabled, and with a valid PDE */
1639 ASSERT(KeAreAllApcsDisabled() == TRUE);
1640 ASSERT(PointerPde->u.Hard.Valid == 1);
1641 }
1642 else
1643 {
1644 /* Not yet implemented in ReactOS */
1645 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1646 }
1647
1648 /* Now capture the PTE. Ignore virtual faults for now */
1649 TempPte = *PointerPte;
1650 ASSERT(TempPte.u.Hard.Valid == 0);
1651
1652 /* Quick check for demand-zero */
1653 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1654 {
1655 /* Resolve the fault */
1656 MiResolveDemandZeroFault(Address,
1657 PointerPte,
1658 CurrentProcess,
1659 MM_NOIRQL);
1660
1661 /* Return the status */
1662 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1663 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1664 }
1665
1666 /* Check for zero PTE */
1667 if (TempPte.u.Long == 0)
1668 {
1669 /* Check if this address range belongs to a valid allocation (VAD) */
1670 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1671 if (ProtectionCode == MM_NOACCESS)
1672 {
1673 #if (_MI_PAGING_LEVELS == 2)
1674 /* Could be a page table for paged pool */
1675 MiCheckPdeForPagedPool(Address);
1676 #endif
1677 /* Has the code above changed anything -- is this now a valid PTE? */
1678 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1679
1680 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1681 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1682 return Status;
1683 }
1684
1685 /* No guard page support yet */
1686 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1687
1688 /*
1689 * Check if this is a real user-mode address or actually a kernel-mode
1690 * page table for a user mode address
1691 */
1692 if (Address <= MM_HIGHEST_USER_ADDRESS)
1693 {
1694 /* Add an additional page table reference */
1695 MiIncrementPageTableReferences(Address);
1696 }
1697
1698 /* Did we get a prototype PTE back? */
1699 if (!ProtoPte)
1700 {
1701 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1702 if (PointerPde == MiAddressToPde(PTE_BASE))
1703 {
1704 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1705 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1706 }
1707 else
1708 {
1709 /* No, create a new PTE. First, write the protection */
1710 PointerPte->u.Soft.Protection = ProtectionCode;
1711 }
1712
1713 /* Lock the PFN database since we're going to grab a page */
1714 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1715
1716 /* Make sure we have enough pages */
1717 ASSERT(MmAvailablePages >= 32);
1718
1719 /* Try to get a zero page */
1720 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1721 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1722 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1723 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1724 if (!PageFrameIndex)
1725 {
1726 /* Grab a page out of there. Later we should grab a colored zero page */
1727 PageFrameIndex = MiRemoveAnyPage(Color);
1728 ASSERT(PageFrameIndex);
1729
1730 /* Release the lock since we need to do some zeroing */
1731 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1732
1733 /* Zero out the page, since it's for user-mode */
1734 MiZeroPfn(PageFrameIndex);
1735
1736 /* Grab the lock again so we can initialize the PFN entry */
1737 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1738 }
1739
1740 /* Initialize the PFN entry now */
1741 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1742
1743 /* And we're done with the lock */
1744 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1745
1746 /* Increment the count of pages in the process */
1747 CurrentProcess->NumberOfPrivatePages++;
1748
1749 /* One more demand-zero fault */
1750 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1751
1752 /* Fault on user PDE, or fault on user PTE? */
1753 if (PointerPte <= MiHighestUserPte)
1754 {
1755 /* User fault, build a user PTE */
1756 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1757 PointerPte,
1758 PointerPte->u.Soft.Protection,
1759 PageFrameIndex);
1760 }
1761 else
1762 {
1763 /* This is a user-mode PDE, create a kernel PTE for it */
1764 MI_MAKE_HARDWARE_PTE(&TempPte,
1765 PointerPte,
1766 PointerPte->u.Soft.Protection,
1767 PageFrameIndex);
1768 }
1769
1770 /* Write the dirty bit for writeable pages */
1771 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1772
1773 /* And now write down the PTE, making the address valid */
1774 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1775 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1776 ASSERT(Pfn1->u1.Event == NULL);
1777
1778 /* Demand zero */
1779 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1780 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1781 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1782 }
1783
1784 /* No guard page support yet */
1785 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1786 ASSERT(ProtectionCode != 0x100);
1787
1788 /* Write the prototype PTE */
1789 TempPte = PrototypePte;
1790 TempPte.u.Soft.Protection = ProtectionCode;
1791 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1792 }
1793 else
1794 {
1795 /* Get the protection code and check if this is a proto PTE */
1796 ProtectionCode = TempPte.u.Soft.Protection;
1797 if (TempPte.u.Soft.Prototype)
1798 {
1799 /* Do we need to go find the real PTE? */
1800 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1801 {
1802 /* Get the prototype pte and VAD for it */
1803 ProtoPte = MiCheckVirtualAddress(Address,
1804 &ProtectionCode,
1805 &Vad);
1806 if (!ProtoPte)
1807 {
1808 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1809 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1810 return STATUS_ACCESS_VIOLATION;
1811 }
1812 }
1813 else
1814 {
1815 /* Get the prototype PTE! */
1816 ProtoPte = MiProtoPteToPte(&TempPte);
1817
1818 /* Is it read-only */
1819 if (TempPte.u.Proto.ReadOnly)
1820 {
1821 /* Set read-only code */
1822 ProtectionCode = MM_READONLY;
1823 }
1824 else
1825 {
1826 /* Set unknown protection */
1827 ProtectionCode = 0x100;
1828 ASSERT(CurrentProcess->CloneRoot != NULL);
1829 }
1830 }
1831 }
1832 }
1833
1834 /* FIXME: Run MiAccessCheck */
1835
1836 /* Dispatch the fault */
1837 Status = MiDispatchFault(StoreInstruction,
1838 Address,
1839 PointerPte,
1840 ProtoPte,
1841 FALSE,
1842 CurrentProcess,
1843 TrapInformation,
1844 Vad);
1845
1846 /* Return the status */
1847 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1848 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1849 return Status;
1850 }
1851
1852 NTSTATUS
1853 NTAPI
1854 MmGetExecuteOptions(IN PULONG ExecuteOptions)
1855 {
1856 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1857 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1858
1859 *ExecuteOptions = 0;
1860
1861 if (CurrentProcess->Flags.ExecuteDisable)
1862 {
1863 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
1864 }
1865
1866 if (CurrentProcess->Flags.ExecuteEnable)
1867 {
1868 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
1869 }
1870
1871 if (CurrentProcess->Flags.DisableThunkEmulation)
1872 {
1873 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
1874 }
1875
1876 if (CurrentProcess->Flags.Permanent)
1877 {
1878 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
1879 }
1880
1881 if (CurrentProcess->Flags.ExecuteDispatchEnable)
1882 {
1883 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
1884 }
1885
1886 if (CurrentProcess->Flags.ImageDispatchEnable)
1887 {
1888 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
1889 }
1890
1891 return STATUS_SUCCESS;
1892 }
1893
1894 NTSTATUS
1895 NTAPI
1896 MmSetExecuteOptions(IN ULONG ExecuteOptions)
1897 {
1898 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1899 KLOCK_QUEUE_HANDLE ProcessLock;
1900 NTSTATUS Status = STATUS_ACCESS_DENIED;
1901 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1902
1903 /* Only accept valid flags */
1904 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
1905 {
1906 /* Fail */
1907 DPRINT1("Invalid no-execute options\n");
1908 return STATUS_INVALID_PARAMETER;
1909 }
1910
1911 /* Change the NX state in the process lock */
1912 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
1913
1914 /* Don't change anything if the permanent flag was set */
1915 if (!CurrentProcess->Flags.Permanent)
1916 {
1917 /* Start by assuming it's not disabled */
1918 CurrentProcess->Flags.ExecuteDisable = FALSE;
1919
1920 /* Now process each flag and turn the equivalent bit on */
1921 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
1922 {
1923 CurrentProcess->Flags.ExecuteDisable = TRUE;
1924 }
1925 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
1926 {
1927 CurrentProcess->Flags.ExecuteEnable = TRUE;
1928 }
1929 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
1930 {
1931 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
1932 }
1933 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
1934 {
1935 CurrentProcess->Flags.Permanent = TRUE;
1936 }
1937 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
1938 {
1939 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1940 }
1941 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
1942 {
1943 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1944 }
1945
1946 /* These are turned on by default if no-execution is also eanbled */
1947 if (CurrentProcess->Flags.ExecuteEnable)
1948 {
1949 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1950 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1951 }
1952
1953 /* All good */
1954 Status = STATUS_SUCCESS;
1955 }
1956
1957 /* Release the lock and return status */
1958 KiReleaseProcessLock(&ProcessLock);
1959 return Status;
1960 }
1961
1962 /* EOF */