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