Merge my current work done on the kd++ branch:
[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 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
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;
880 PFN_COUNT PteCount, ProcessedPtes;
881 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
882 Address,
883 Process);
884
885 /* Make sure the addresses are ok */
886 ASSERT(PointerPte == MiAddressToPte(Address));
887
888 //
889 // Make sure APCs are off and we're not at dispatch
890 //
891 OldIrql = KeGetCurrentIrql();
892 ASSERT(OldIrql <= APC_LEVEL);
893 ASSERT(KeAreAllApcsDisabled() == TRUE);
894
895 //
896 // Grab a copy of the PTE
897 //
898 TempPte = *PointerPte;
899
900 /* Do we have a prototype PTE? */
901 if (PointerProtoPte)
902 {
903 /* This should never happen */
904 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
905
906 /* Check if this is a kernel-mode address */
907 SuperProtoPte = MiAddressToPte(PointerProtoPte);
908 if (Address >= MmSystemRangeStart)
909 {
910 /* Lock the PFN database */
911 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
912
913 /* Has the PTE been made valid yet? */
914 if (!SuperProtoPte->u.Hard.Valid)
915 {
916 ASSERT(FALSE);
917 }
918 else if (PointerPte->u.Hard.Valid == 1)
919 {
920 ASSERT(FALSE);
921 }
922
923 /* Resolve the fault -- this will release the PFN lock */
924 Status = MiResolveProtoPteFault(StoreInstruction,
925 Address,
926 PointerPte,
927 PointerProtoPte,
928 &OutPfn,
929 NULL,
930 NULL,
931 Process,
932 LockIrql,
933 TrapInformation);
934 ASSERT(Status == STATUS_SUCCESS);
935
936 /* Complete this as a transition fault */
937 ASSERT(OldIrql == KeGetCurrentIrql());
938 ASSERT(OldIrql <= APC_LEVEL);
939 ASSERT(KeAreAllApcsDisabled() == TRUE);
940 return Status;
941 }
942 else
943 {
944 /* We only handle the lookup path */
945 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
946
947 /* Is there a non-image VAD? */
948 if ((Vad) &&
949 (Vad->u.VadFlags.VadType != VadImageMap) &&
950 !(Vad->u2.VadFlags2.ExtendableFile))
951 {
952 /* One day, ReactOS will cluster faults */
953 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
954 DPRINT("Should cluster fault, but won't\n");
955 }
956
957 /* Only one PTE to handle for now */
958 PteCount = 1;
959 ProcessedPtes = 0;
960
961 /* Lock the PFN database */
962 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
963
964 /* We only handle the valid path */
965 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
966
967 /* Capture the PTE */
968 TempPte = *PointerProtoPte;
969
970 /* Loop to handle future case of clustered faults */
971 while (TRUE)
972 {
973 /* For our current usage, this should be true */
974 if (TempPte.u.Hard.Valid == 1)
975 {
976 /* Bump the share count on the PTE */
977 PageFrameIndex = PFN_FROM_PTE(&TempPte);
978 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
979 Pfn1->u2.ShareCount++;
980 }
981 else if ((TempPte.u.Soft.Prototype == 0) &&
982 (TempPte.u.Soft.Transition == 1))
983 {
984 /* This is a standby page, bring it back from the cache */
985 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
986 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
987 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
988 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
989
990 /* Should not yet happen in ReactOS */
991 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
992 ASSERT(Pfn1->u4.InPageError == 0);
993
994 /* Get the page */
995 MiUnlinkPageFromList(Pfn1);
996
997 /* Bump its reference count */
998 ASSERT(Pfn1->u2.ShareCount == 0);
999 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1000 Pfn1->u2.ShareCount++;
1001
1002 /* Make it valid again */
1003 /* This looks like another macro.... */
1004 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1005 ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1006 ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1007 ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1008 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1009 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1010 TempPte.u.Hard.Valid = 1;
1011 TempPte.u.Hard.Accessed = 1;
1012
1013 /* Is the PTE writeable? */
1014 if (((Pfn1->u3.e1.Modified) && (TempPte.u.Hard.Write)) &&
1015 (TempPte.u.Hard.CopyOnWrite == 0))
1016 {
1017 /* Make it dirty */
1018 TempPte.u.Hard.Dirty = TRUE;
1019 }
1020 else
1021 {
1022 /* Make it clean */
1023 TempPte.u.Hard.Dirty = FALSE;
1024 }
1025
1026 /* Write the valid PTE */
1027 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1028 ASSERT(PointerPte->u.Hard.Valid == 0);
1029 }
1030 else
1031 {
1032 /* Page is invalid, get out of the loop */
1033 break;
1034 }
1035
1036 /* One more done, was it the last? */
1037 if (++ProcessedPtes == PteCount)
1038 {
1039 /* Complete the fault */
1040 MiCompleteProtoPteFault(StoreInstruction,
1041 Address,
1042 PointerPte,
1043 PointerProtoPte,
1044 LockIrql,
1045 &OutPfn);
1046
1047 /* THIS RELEASES THE PFN LOCK! */
1048 break;
1049 }
1050
1051 /* No clustered faults yet */
1052 ASSERT(FALSE);
1053 }
1054
1055 /* Did we resolve the fault? */
1056 if (ProcessedPtes)
1057 {
1058 /* Bump the transition count */
1059 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1060 ProcessedPtes--;
1061
1062 /* Loop all the processing we did */
1063 ASSERT(ProcessedPtes == 0);
1064
1065 /* Complete this as a transition fault */
1066 ASSERT(OldIrql == KeGetCurrentIrql());
1067 ASSERT(OldIrql <= APC_LEVEL);
1068 ASSERT(KeAreAllApcsDisabled() == TRUE);
1069 return STATUS_PAGE_FAULT_TRANSITION;
1070 }
1071
1072 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1073 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1074 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1075 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1076 ASSERT(PointerPte->u.Hard.Valid == 0);
1077
1078 /* Resolve the fault -- this will release the PFN lock */
1079 Status = MiResolveProtoPteFault(StoreInstruction,
1080 Address,
1081 PointerPte,
1082 PointerProtoPte,
1083 &OutPfn,
1084 NULL,
1085 NULL,
1086 Process,
1087 LockIrql,
1088 TrapInformation);
1089 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1090 //ASSERT(Status != STATUS_REFAULT);
1091 //ASSERT(Status != STATUS_PTE_CHANGED);
1092
1093 /* Did the routine clean out the PFN or should we? */
1094 if (OutPfn)
1095 {
1096 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1097 ASSERT(PointerProtoPte != NULL);
1098 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1099
1100 /* Dereference the locked PFN */
1101 MiDereferencePfnAndDropLockCount(OutPfn);
1102 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1103
1104 /* And now release the lock */
1105 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1106 }
1107
1108 /* Complete this as a transition fault */
1109 ASSERT(OldIrql == KeGetCurrentIrql());
1110 ASSERT(OldIrql <= APC_LEVEL);
1111 ASSERT(KeAreAllApcsDisabled() == TRUE);
1112 return Status;
1113 }
1114 }
1115
1116 //
1117 // The PTE must be invalid but not completely empty. It must also not be a
1118 // prototype PTE as that scenario should've been handled above. These are
1119 // all Windows checks
1120 //
1121 ASSERT(TempPte.u.Hard.Valid == 0);
1122 ASSERT(TempPte.u.Soft.Prototype == 0);
1123 ASSERT(TempPte.u.Long != 0);
1124
1125 //
1126 // No transition or page file software PTEs in ARM3 yet, so this must be a
1127 // demand zero page. These are all ReactOS checks
1128 //
1129 ASSERT(TempPte.u.Soft.Transition == 0);
1130 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1131
1132 //
1133 // If we got this far, the PTE can only be a demand zero PTE, which is what
1134 // we want. Go handle it!
1135 //
1136 Status = MiResolveDemandZeroFault(Address,
1137 PointerPte,
1138 Process,
1139 MM_NOIRQL);
1140 ASSERT(KeAreAllApcsDisabled() == TRUE);
1141 if (NT_SUCCESS(Status))
1142 {
1143 //
1144 // Make sure we're returning in a sane state and pass the status down
1145 //
1146 ASSERT(OldIrql == KeGetCurrentIrql());
1147 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1148 return Status;
1149 }
1150
1151 //
1152 // Generate an access fault
1153 //
1154 return STATUS_ACCESS_VIOLATION;
1155 }
1156
1157 NTSTATUS
1158 NTAPI
1159 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1160 IN PVOID Address,
1161 IN KPROCESSOR_MODE Mode,
1162 IN PVOID TrapInformation)
1163 {
1164 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1165 PMMPTE ProtoPte = NULL;
1166 PMMPTE PointerPte = MiAddressToPte(Address);
1167 PMMPDE PointerPde = MiAddressToPde(Address);
1168 #if (_MI_PAGING_LEVELS >= 3)
1169 PMMPDE PointerPpe = MiAddressToPpe(Address);
1170 #if (_MI_PAGING_LEVELS == 4)
1171 PMMPDE PointerPxe = MiAddressToPxe(Address);
1172 #endif
1173 #endif
1174 MMPTE TempPte;
1175 PETHREAD CurrentThread;
1176 PEPROCESS CurrentProcess;
1177 NTSTATUS Status;
1178 PMMSUPPORT WorkingSet;
1179 ULONG ProtectionCode;
1180 PMMVAD Vad;
1181 PFN_NUMBER PageFrameIndex;
1182 ULONG Color;
1183 BOOLEAN IsSessionAddress;
1184 PMMPFN Pfn1;
1185 DPRINT("ARM3 FAULT AT: %p\n", Address);
1186
1187 /* Check for page fault on high IRQL */
1188 if (OldIrql > APC_LEVEL)
1189 {
1190 #if (_MI_PAGING_LEVELS < 3)
1191 /* Could be a page table for paged pool, which we'll allow */
1192 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1193 MiCheckPdeForPagedPool(Address);
1194 #endif
1195 /* Check if any of the top-level pages are invalid */
1196 if (
1197 #if (_MI_PAGING_LEVELS == 4)
1198 (PointerPxe->u.Hard.Valid == 0) ||
1199 #endif
1200 #if (_MI_PAGING_LEVELS >= 3)
1201 (PointerPpe->u.Hard.Valid == 0) ||
1202 #endif
1203 (PointerPde->u.Hard.Valid == 0))
1204 {
1205 /* This fault is not valid, printf out some debugging help */
1206 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1207 Address,
1208 OldIrql);
1209 if (TrapInformation)
1210 {
1211 PKTRAP_FRAME TrapFrame = TrapInformation;
1212 #ifdef _M_IX86
1213 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1214 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1215 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1216 #elif defined(_M_AMD64)
1217 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1218 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1219 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1220 #endif
1221 }
1222
1223 /* Tell the trap handler to fail */
1224 return STATUS_IN_PAGE_ERROR | 0x10000000;
1225 }
1226
1227 /* Not yet implemented in ReactOS */
1228 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1229 ASSERT(((StoreInstruction) && (PointerPte->u.Hard.CopyOnWrite)) == FALSE);
1230
1231 /* Check if this was a write */
1232 if (StoreInstruction)
1233 {
1234 /* Was it to a read-only page? */
1235 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1236 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1237 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1238 {
1239 /* Crash with distinguished bugcheck code */
1240 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1241 (ULONG_PTR)Address,
1242 PointerPte->u.Long,
1243 (ULONG_PTR)TrapInformation,
1244 10);
1245 }
1246 }
1247
1248 /* Nothing is actually wrong */
1249 DPRINT1("Fault at IRQL1 is ok\n");
1250 return STATUS_SUCCESS;
1251 }
1252
1253 /* Check for kernel fault address */
1254 if (Address >= MmSystemRangeStart)
1255 {
1256 /* Bail out, if the fault came from user mode */
1257 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1258
1259 #if (_MI_PAGING_LEVELS == 4)
1260 /* AMD64 system, check if PXE is invalid */
1261 if (PointerPxe->u.Hard.Valid == 0)
1262 {
1263 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1264 (ULONG_PTR)Address,
1265 StoreInstruction,
1266 (ULONG_PTR)TrapInformation,
1267 7);
1268 }
1269 #endif
1270 #if (_MI_PAGING_LEVELS == 4)
1271 /* PAE/AMD64 system, check if PPE is invalid */
1272 if (PointerPpe->u.Hard.Valid == 0)
1273 {
1274 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1275 (ULONG_PTR)Address,
1276 StoreInstruction,
1277 (ULONG_PTR)TrapInformation,
1278 5);
1279 }
1280 #endif
1281 #if (_MI_PAGING_LEVELS == 2)
1282 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1283 MiCheckPdeForPagedPool(Address);
1284 #endif
1285
1286 /* Check if the PDE is invalid */
1287 if (PointerPde->u.Hard.Valid == 0)
1288 {
1289 /* PDE (still) not valid, kill the system */
1290 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1291 (ULONG_PTR)Address,
1292 StoreInstruction,
1293 (ULONG_PTR)TrapInformation,
1294 2);
1295 }
1296
1297 /* Not handling session faults yet */
1298 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1299
1300 /* The PDE is valid, so read the PTE */
1301 TempPte = *PointerPte;
1302 if (TempPte.u.Hard.Valid == 1)
1303 {
1304 /* Check if this was system space or session space */
1305 if (!IsSessionAddress)
1306 {
1307 /* Check if the PTE is still valid under PFN lock */
1308 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1309 TempPte = *PointerPte;
1310 if (TempPte.u.Hard.Valid)
1311 {
1312 /* Check if this was a write */
1313 if (StoreInstruction)
1314 {
1315 /* Was it to a read-only page? */
1316 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1317 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1318 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1319 {
1320 /* Crash with distinguished bugcheck code */
1321 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1322 (ULONG_PTR)Address,
1323 PointerPte->u.Long,
1324 (ULONG_PTR)TrapInformation,
1325 11);
1326 }
1327 }
1328 }
1329
1330 /* Release PFN lock and return all good */
1331 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1332 return STATUS_SUCCESS;
1333 }
1334 }
1335
1336 /* Check if this was a session PTE that needs to remap the session PDE */
1337 if (MI_IS_SESSION_PTE(Address))
1338 {
1339 /* Do the remapping */
1340 Status = MiCheckPdeForSessionSpace(Address);
1341 if (!NT_SUCCESS(Status))
1342 {
1343 /* It failed, this address is invalid */
1344 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1345 (ULONG_PTR)Address,
1346 StoreInstruction,
1347 (ULONG_PTR)TrapInformation,
1348 6);
1349 }
1350 }
1351
1352 /* Check for a fault on the page table or hyperspace */
1353 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1354 {
1355 #if (_MI_PAGING_LEVELS < 3)
1356 /* Windows does this check but I don't understand why -- it's done above! */
1357 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1358 #endif
1359 /* Handle this as a user mode fault */
1360 goto UserFault;
1361 }
1362
1363 /* Get the current thread */
1364 CurrentThread = PsGetCurrentThread();
1365
1366 /* What kind of address is this */
1367 if (!IsSessionAddress)
1368 {
1369 /* Use the system working set */
1370 WorkingSet = &MmSystemCacheWs;
1371 CurrentProcess = NULL;
1372
1373 /* Make sure we don't have a recursive working set lock */
1374 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1375 (CurrentThread->OwnsProcessWorkingSetShared) ||
1376 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1377 (CurrentThread->OwnsSystemWorkingSetShared) ||
1378 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1379 (CurrentThread->OwnsSessionWorkingSetShared))
1380 {
1381 /* Fail */
1382 return STATUS_IN_PAGE_ERROR | 0x10000000;
1383 }
1384 }
1385 else
1386 {
1387 /* Use the session process and working set */
1388 CurrentProcess = HYDRA_PROCESS;
1389 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1390
1391 /* Make sure we don't have a recursive working set lock */
1392 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1393 (CurrentThread->OwnsSessionWorkingSetShared))
1394 {
1395 /* Fail */
1396 return STATUS_IN_PAGE_ERROR | 0x10000000;
1397 }
1398 }
1399
1400 /* Acquire the working set lock */
1401 KeRaiseIrql(APC_LEVEL, &LockIrql);
1402 MiLockWorkingSet(CurrentThread, WorkingSet);
1403
1404 /* Re-read PTE now that we own the lock */
1405 TempPte = *PointerPte;
1406 if (TempPte.u.Hard.Valid == 1)
1407 {
1408 /* Check if this was a write */
1409 if (StoreInstruction)
1410 {
1411 /* Was it to a read-only page that is not copy on write? */
1412 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1413 if (!(TempPte.u.Long & PTE_READWRITE) &&
1414 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1415 !(TempPte.u.Hard.CopyOnWrite))
1416 {
1417 /* Case not yet handled */
1418 ASSERT(!IsSessionAddress);
1419
1420 /* Crash with distinguished bugcheck code */
1421 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1422 (ULONG_PTR)Address,
1423 TempPte.u.Long,
1424 (ULONG_PTR)TrapInformation,
1425 12);
1426 }
1427 }
1428
1429 /* Check for read-only write in session space */
1430 if ((IsSessionAddress) &&
1431 (StoreInstruction) &&
1432 !(TempPte.u.Hard.Write))
1433 {
1434 /* Sanity check */
1435 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1436
1437 /* Was this COW? */
1438 if (TempPte.u.Hard.CopyOnWrite == 0)
1439 {
1440 /* Then this is not allowed */
1441 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1442 (ULONG_PTR)Address,
1443 (ULONG_PTR)TempPte.u.Long,
1444 (ULONG_PTR)TrapInformation,
1445 13);
1446 }
1447
1448 /* Otherwise, handle COW */
1449 ASSERT(FALSE);
1450 }
1451
1452 /* Release the working set */
1453 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1454 KeLowerIrql(LockIrql);
1455
1456 /* Otherwise, the PDE was probably invalid, and all is good now */
1457 return STATUS_SUCCESS;
1458 }
1459
1460 /* Check one kind of prototype PTE */
1461 if (TempPte.u.Soft.Prototype)
1462 {
1463 /* Make sure protected pool is on, and that this is a pool address */
1464 if ((MmProtectFreedNonPagedPool) &&
1465 (((Address >= MmNonPagedPoolStart) &&
1466 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1467 MmSizeOfNonPagedPoolInBytes))) ||
1468 ((Address >= MmNonPagedPoolExpansionStart) &&
1469 (Address < MmNonPagedPoolEnd))))
1470 {
1471 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1472 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1473 (ULONG_PTR)Address,
1474 StoreInstruction,
1475 Mode,
1476 4);
1477 }
1478
1479 /* Get the prototype PTE! */
1480 ProtoPte = MiProtoPteToPte(&TempPte);
1481
1482 /* Do we need to locate the prototype PTE in session space? */
1483 if ((IsSessionAddress) &&
1484 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1485 {
1486 /* Yep, go find it as well as the VAD for it */
1487 ProtoPte = MiCheckVirtualAddress(Address,
1488 &ProtectionCode,
1489 &Vad);
1490 ASSERT(ProtoPte != NULL);
1491 }
1492 }
1493 else
1494 {
1495 /* We don't implement transition PTEs */
1496 ASSERT(TempPte.u.Soft.Transition == 0);
1497
1498 /* Check for no-access PTE */
1499 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1500 {
1501 /* Bugcheck the system! */
1502 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1503 (ULONG_PTR)Address,
1504 StoreInstruction,
1505 (ULONG_PTR)TrapInformation,
1506 1);
1507 }
1508 }
1509
1510 /* Check for demand page */
1511 if ((StoreInstruction) &&
1512 !(ProtoPte) &&
1513 !(IsSessionAddress) &&
1514 !(TempPte.u.Hard.Valid))
1515 {
1516 /* Get the protection code */
1517 ASSERT(TempPte.u.Soft.Transition == 0);
1518 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1519 {
1520 /* Bugcheck the system! */
1521 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1522 (ULONG_PTR)Address,
1523 TempPte.u.Long,
1524 (ULONG_PTR)TrapInformation,
1525 14);
1526 }
1527 }
1528
1529 /* Now do the real fault handling */
1530 Status = MiDispatchFault(StoreInstruction,
1531 Address,
1532 PointerPte,
1533 ProtoPte,
1534 FALSE,
1535 CurrentProcess,
1536 TrapInformation,
1537 NULL);
1538
1539 /* Release the working set */
1540 ASSERT(KeAreAllApcsDisabled() == TRUE);
1541 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1542 KeLowerIrql(LockIrql);
1543
1544 /* We are done! */
1545 DPRINT("Fault resolved with status: %lx\n", Status);
1546 return Status;
1547 }
1548
1549 /* This is a user fault */
1550 UserFault:
1551 CurrentThread = PsGetCurrentThread();
1552 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
1553
1554 /* Lock the working set */
1555 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
1556
1557 #if (_MI_PAGING_LEVELS == 4)
1558 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1559 // also this is missing the page count increment
1560 /* Check if the PXE is valid */
1561 if (PointerPxe->u.Hard.Valid == 0)
1562 {
1563 /* Right now, we only handle scenarios where the PXE is totally empty */
1564 ASSERT(PointerPxe->u.Long == 0);
1565 #if 0
1566 /* Resolve a demand zero fault */
1567 Status = MiResolveDemandZeroFault(PointerPpe,
1568 MM_READWRITE,
1569 CurrentProcess,
1570 MM_NOIRQL);
1571 #endif
1572 /* We should come back with a valid PXE */
1573 ASSERT(PointerPxe->u.Hard.Valid == 1);
1574 }
1575 #endif
1576
1577 #if (_MI_PAGING_LEVELS >= 3)
1578 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
1579 // also this is missing the page count increment
1580 /* Check if the PPE is valid */
1581 if (PointerPpe->u.Hard.Valid == 0)
1582 {
1583 /* Right now, we only handle scenarios where the PPE is totally empty */
1584 ASSERT(PointerPpe->u.Long == 0);
1585 #if 0
1586 /* Resolve a demand zero fault */
1587 Status = MiResolveDemandZeroFault(PointerPde,
1588 MM_READWRITE,
1589 CurrentProcess,
1590 MM_NOIRQL);
1591 #endif
1592 /* We should come back with a valid PPE */
1593 ASSERT(PointerPpe->u.Hard.Valid == 1);
1594 }
1595 #endif
1596
1597 /* Check if the PDE is valid */
1598 if (PointerPde->u.Hard.Valid == 0)
1599 {
1600 /* Right now, we only handle scenarios where the PDE is totally empty */
1601 ASSERT(PointerPde->u.Long == 0);
1602
1603 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
1604 #if MI_TRACE_PFNS
1605 UserPdeFault = TRUE;
1606 #endif
1607 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1608 if (ProtectionCode == MM_NOACCESS)
1609 {
1610 #if (_MI_PAGING_LEVELS == 2)
1611 /* Could be a page table for paged pool */
1612 MiCheckPdeForPagedPool(Address);
1613 #endif
1614 /* Has the code above changed anything -- is this now a valid PTE? */
1615 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1616
1617 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1618 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1619 return Status;
1620 }
1621
1622 /* Write a demand-zero PDE */
1623 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
1624
1625 /* Dispatch the fault */
1626 Status = MiDispatchFault(TRUE,
1627 PointerPte,
1628 PointerPde,
1629 NULL,
1630 FALSE,
1631 PsGetCurrentProcess(),
1632 TrapInformation,
1633 NULL);
1634 #if MI_TRACE_PFNS
1635 UserPdeFault = FALSE;
1636 #endif
1637 /* We should come back with APCs enabled, and with a valid PDE */
1638 ASSERT(KeAreAllApcsDisabled() == TRUE);
1639 ASSERT(PointerPde->u.Hard.Valid == 1);
1640 }
1641 else
1642 {
1643 /* Not yet implemented in ReactOS */
1644 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1645 }
1646
1647 /* Now capture the PTE. Ignore virtual faults for now */
1648 TempPte = *PointerPte;
1649 ASSERT(TempPte.u.Hard.Valid == 0);
1650
1651 /* Quick check for demand-zero */
1652 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
1653 {
1654 /* Resolve the fault */
1655 MiResolveDemandZeroFault(Address,
1656 PointerPte,
1657 CurrentProcess,
1658 MM_NOIRQL);
1659
1660 /* Return the status */
1661 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1662 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1663 }
1664
1665 /* Check for zero PTE */
1666 if (TempPte.u.Long == 0)
1667 {
1668 /* Check if this address range belongs to a valid allocation (VAD) */
1669 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
1670 if (ProtectionCode == MM_NOACCESS)
1671 {
1672 #if (_MI_PAGING_LEVELS == 2)
1673 /* Could be a page table for paged pool */
1674 MiCheckPdeForPagedPool(Address);
1675 #endif
1676 /* Has the code above changed anything -- is this now a valid PTE? */
1677 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
1678
1679 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
1680 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1681 return Status;
1682 }
1683
1684 /* No guard page support yet */
1685 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1686
1687 /*
1688 * Check if this is a real user-mode address or actually a kernel-mode
1689 * page table for a user mode address
1690 */
1691 if (Address <= MM_HIGHEST_USER_ADDRESS)
1692 {
1693 /* Add an additional page table reference */
1694 MiIncrementPageTableReferences(Address);
1695 }
1696
1697 /* Did we get a prototype PTE back? */
1698 if (!ProtoPte)
1699 {
1700 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
1701 if (PointerPde == MiAddressToPde(PTE_BASE))
1702 {
1703 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
1704 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
1705 }
1706 else
1707 {
1708 /* No, create a new PTE. First, write the protection */
1709 PointerPte->u.Soft.Protection = ProtectionCode;
1710 }
1711
1712 /* Lock the PFN database since we're going to grab a page */
1713 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1714
1715 /* Make sure we have enough pages */
1716 ASSERT(MmAvailablePages >= 32);
1717
1718 /* Try to get a zero page */
1719 MI_SET_USAGE(MI_USAGE_PEB_TEB);
1720 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
1721 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
1722 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1723 if (!PageFrameIndex)
1724 {
1725 /* Grab a page out of there. Later we should grab a colored zero page */
1726 PageFrameIndex = MiRemoveAnyPage(Color);
1727 ASSERT(PageFrameIndex);
1728
1729 /* Release the lock since we need to do some zeroing */
1730 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1731
1732 /* Zero out the page, since it's for user-mode */
1733 MiZeroPfn(PageFrameIndex);
1734
1735 /* Grab the lock again so we can initialize the PFN entry */
1736 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1737 }
1738
1739 /* Initialize the PFN entry now */
1740 MiInitializePfn(PageFrameIndex, PointerPte, 1);
1741
1742 /* And we're done with the lock */
1743 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1744
1745 /* Increment the count of pages in the process */
1746 CurrentProcess->NumberOfPrivatePages++;
1747
1748 /* One more demand-zero fault */
1749 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
1750
1751 /* Fault on user PDE, or fault on user PTE? */
1752 if (PointerPte <= MiHighestUserPte)
1753 {
1754 /* User fault, build a user PTE */
1755 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1756 PointerPte,
1757 PointerPte->u.Soft.Protection,
1758 PageFrameIndex);
1759 }
1760 else
1761 {
1762 /* This is a user-mode PDE, create a kernel PTE for it */
1763 MI_MAKE_HARDWARE_PTE(&TempPte,
1764 PointerPte,
1765 PointerPte->u.Soft.Protection,
1766 PageFrameIndex);
1767 }
1768
1769 /* Write the dirty bit for writeable pages */
1770 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
1771
1772 /* And now write down the PTE, making the address valid */
1773 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1774 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1775 ASSERT(Pfn1->u1.Event == NULL);
1776
1777 /* Demand zero */
1778 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1779 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1780 return STATUS_PAGE_FAULT_DEMAND_ZERO;
1781 }
1782
1783 /* No guard page support yet */
1784 ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
1785 ASSERT(ProtectionCode != 0x100);
1786
1787 /* Write the prototype PTE */
1788 TempPte = PrototypePte;
1789 TempPte.u.Soft.Protection = ProtectionCode;
1790 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1791 }
1792 else
1793 {
1794 /* Get the protection code and check if this is a proto PTE */
1795 ProtectionCode = TempPte.u.Soft.Protection;
1796 if (TempPte.u.Soft.Prototype)
1797 {
1798 /* Do we need to go find the real PTE? */
1799 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1800 {
1801 /* Get the prototype pte and VAD for it */
1802 ProtoPte = MiCheckVirtualAddress(Address,
1803 &ProtectionCode,
1804 &Vad);
1805 if (!ProtoPte)
1806 {
1807 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1808 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1809 return STATUS_ACCESS_VIOLATION;
1810 }
1811 }
1812 else
1813 {
1814 /* Get the prototype PTE! */
1815 ProtoPte = MiProtoPteToPte(&TempPte);
1816
1817 /* Is it read-only */
1818 if (TempPte.u.Proto.ReadOnly)
1819 {
1820 /* Set read-only code */
1821 ProtectionCode = MM_READONLY;
1822 }
1823 else
1824 {
1825 /* Set unknown protection */
1826 ProtectionCode = 0x100;
1827 ASSERT(CurrentProcess->CloneRoot != NULL);
1828 }
1829 }
1830 }
1831 }
1832
1833 /* FIXME: Run MiAccessCheck */
1834
1835 /* Dispatch the fault */
1836 Status = MiDispatchFault(StoreInstruction,
1837 Address,
1838 PointerPte,
1839 ProtoPte,
1840 FALSE,
1841 CurrentProcess,
1842 TrapInformation,
1843 Vad);
1844
1845 /* Return the status */
1846 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1847 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
1848 return Status;
1849 }
1850
1851 NTSTATUS
1852 NTAPI
1853 MmGetExecuteOptions(IN PULONG ExecuteOptions)
1854 {
1855 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1856 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1857
1858 *ExecuteOptions = 0;
1859
1860 if (CurrentProcess->Flags.ExecuteDisable)
1861 {
1862 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
1863 }
1864
1865 if (CurrentProcess->Flags.ExecuteEnable)
1866 {
1867 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
1868 }
1869
1870 if (CurrentProcess->Flags.DisableThunkEmulation)
1871 {
1872 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
1873 }
1874
1875 if (CurrentProcess->Flags.Permanent)
1876 {
1877 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
1878 }
1879
1880 if (CurrentProcess->Flags.ExecuteDispatchEnable)
1881 {
1882 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
1883 }
1884
1885 if (CurrentProcess->Flags.ImageDispatchEnable)
1886 {
1887 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
1888 }
1889
1890 return STATUS_SUCCESS;
1891 }
1892
1893 NTSTATUS
1894 NTAPI
1895 MmSetExecuteOptions(IN ULONG ExecuteOptions)
1896 {
1897 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
1898 KLOCK_QUEUE_HANDLE ProcessLock;
1899 NTSTATUS Status = STATUS_ACCESS_DENIED;
1900 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1901
1902 /* Only accept valid flags */
1903 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
1904 {
1905 /* Fail */
1906 DPRINT1("Invalid no-execute options\n");
1907 return STATUS_INVALID_PARAMETER;
1908 }
1909
1910 /* Change the NX state in the process lock */
1911 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
1912
1913 /* Don't change anything if the permanent flag was set */
1914 if (!CurrentProcess->Flags.Permanent)
1915 {
1916 /* Start by assuming it's not disabled */
1917 CurrentProcess->Flags.ExecuteDisable = FALSE;
1918
1919 /* Now process each flag and turn the equivalent bit on */
1920 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
1921 {
1922 CurrentProcess->Flags.ExecuteDisable = TRUE;
1923 }
1924 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
1925 {
1926 CurrentProcess->Flags.ExecuteEnable = TRUE;
1927 }
1928 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
1929 {
1930 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
1931 }
1932 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
1933 {
1934 CurrentProcess->Flags.Permanent = TRUE;
1935 }
1936 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
1937 {
1938 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1939 }
1940 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
1941 {
1942 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1943 }
1944
1945 /* These are turned on by default if no-execution is also eanbled */
1946 if (CurrentProcess->Flags.ExecuteEnable)
1947 {
1948 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
1949 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
1950 }
1951
1952 /* All good */
1953 Status = STATUS_SUCCESS;
1954 }
1955
1956 /* Release the lock and return status */
1957 KiReleaseProcessLock(&ProcessLock);
1958 return Status;
1959 }
1960
1961 /* EOF */