[NTOS/MM]
[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 <mm/ARM3/miarm.h>
17
18 /* GLOBALS ********************************************************************/
19
20 #define HYDRA_PROCESS (PEPROCESS)1
21 #if MI_TRACE_PFNS
22 BOOLEAN UserPdeFault = FALSE;
23 #endif
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 NTSTATUS
28 NTAPI
29 MiCheckForUserStackOverflow(IN PVOID Address,
30 IN PVOID TrapInformation)
31 {
32 PETHREAD CurrentThread = PsGetCurrentThread();
33 PTEB Teb = CurrentThread->Tcb.Teb;
34 PVOID StackBase, DeallocationStack, NextStackAddress;
35 SIZE_T GuranteedSize;
36 NTSTATUS Status;
37
38 /* Do we own the address space lock? */
39 if (CurrentThread->AddressSpaceOwner == 1)
40 {
41 /* This isn't valid */
42 DPRINT1("Process owns address space lock\n");
43 ASSERT(KeAreAllApcsDisabled() == TRUE);
44 return STATUS_GUARD_PAGE_VIOLATION;
45 }
46
47 /* Are we attached? */
48 if (KeIsAttachedProcess())
49 {
50 /* This isn't valid */
51 DPRINT1("Process is attached\n");
52 return STATUS_GUARD_PAGE_VIOLATION;
53 }
54
55 /* Read the current settings */
56 StackBase = Teb->NtTib.StackBase;
57 DeallocationStack = Teb->DeallocationStack;
58 GuranteedSize = Teb->GuaranteedStackBytes;
59 DPRINT("Handling guard page fault with Stacks Addresses 0x%p and 0x%p, guarantee: %lx\n",
60 StackBase, DeallocationStack, GuranteedSize);
61
62 /* Guarantees make this code harder, for now, assume there aren't any */
63 ASSERT(GuranteedSize == 0);
64
65 /* So allocate only the minimum guard page size */
66 GuranteedSize = PAGE_SIZE;
67
68 /* Does this faulting stack address actually exist in the stack? */
69 if ((Address >= StackBase) || (Address < DeallocationStack))
70 {
71 /* That's odd... */
72 DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
73 Address, StackBase, DeallocationStack);
74 return STATUS_GUARD_PAGE_VIOLATION;
75 }
76
77 /* This is where the stack will start now */
78 NextStackAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(Address) - GuranteedSize);
79
80 /* Do we have at least one page between here and the end of the stack? */
81 if (((ULONG_PTR)NextStackAddress - PAGE_SIZE) <= (ULONG_PTR)DeallocationStack)
82 {
83 /* We don't -- Windows would try to make this guard page valid now */
84 DPRINT1("Close to our death...\n");
85 return STATUS_STACK_OVERFLOW;
86 }
87
88 /* Don't handle this flag yet */
89 ASSERT((PsGetCurrentProcess()->Peb->NtGlobalFlag & FLG_DISABLE_STACK_EXTENSION) == 0);
90
91 /* Update the stack limit */
92 Teb->NtTib.StackLimit = (PVOID)((ULONG_PTR)NextStackAddress + GuranteedSize);
93
94 /* Now move the guard page to the next page */
95 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
96 &NextStackAddress,
97 0,
98 &GuranteedSize,
99 MEM_COMMIT,
100 PAGE_READWRITE | PAGE_GUARD);
101 if ((NT_SUCCESS(Status) || (Status == STATUS_ALREADY_COMMITTED)))
102 {
103 /* We did it! */
104 DPRINT("Guard page handled successfully for %p\n", Address);
105 return STATUS_PAGE_FAULT_GUARD_PAGE;
106 }
107
108 /* Fail, we couldn't move the guard page */
109 DPRINT1("Guard page failure: %lx\n", Status);
110 ASSERT(FALSE);
111 return STATUS_STACK_OVERFLOW;
112 }
113
114 FORCEINLINE
115 BOOLEAN
116 MiIsAccessAllowed(
117 _In_ ULONG ProtectionMask,
118 _In_ BOOLEAN Write,
119 _In_ BOOLEAN Execute)
120 {
121 #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
122 (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
123 ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
124 static const UCHAR AccessAllowedMask[2][2] =
125 {
126 { // Protect 0 1 2 3 4 5 6 7
127 _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
128 _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
129 },
130 {
131 _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
132 _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
133 }
134 };
135
136 /* We want only the lower access bits */
137 ProtectionMask &= MM_PROTECT_ACCESS;
138
139 /* Look it up in the table */
140 return (AccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1;
141 }
142
143 NTSTATUS
144 NTAPI
145 MiAccessCheck(IN PMMPTE PointerPte,
146 IN BOOLEAN StoreInstruction,
147 IN KPROCESSOR_MODE PreviousMode,
148 IN ULONG_PTR ProtectionMask,
149 IN PVOID TrapFrame,
150 IN BOOLEAN LockHeld)
151 {
152 MMPTE TempPte;
153
154 /* Check for invalid user-mode access */
155 if ((PreviousMode == UserMode) && (PointerPte > MiHighestUserPte))
156 {
157 return STATUS_ACCESS_VIOLATION;
158 }
159
160 /* Capture the PTE -- is it valid? */
161 TempPte = *PointerPte;
162 if (TempPte.u.Hard.Valid)
163 {
164 /* Was someone trying to write to it? */
165 if (StoreInstruction)
166 {
167 /* Is it writable?*/
168 if (MI_IS_PAGE_WRITEABLE(&TempPte) ||
169 MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
170 {
171 /* Then there's nothing to worry about */
172 return STATUS_SUCCESS;
173 }
174
175 /* Oops! This isn't allowed */
176 return STATUS_ACCESS_VIOLATION;
177 }
178
179 /* Someone was trying to read from a valid PTE, that's fine too */
180 return STATUS_SUCCESS;
181 }
182
183 /* Check if the protection on the page allows what is being attempted */
184 if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE))
185 {
186 return STATUS_ACCESS_VIOLATION;
187 }
188
189 /* Check if this is a guard page */
190 if ((ProtectionMask & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
191 {
192 ASSERT(ProtectionMask != MM_DECOMMIT);
193
194 /* Attached processes can't expand their stack */
195 if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION;
196
197 /* No support for transition PTEs yet */
198 ASSERT(((TempPte.u.Soft.Transition == 1) &&
199 (TempPte.u.Soft.Prototype == 0)) == FALSE);
200
201 /* Remove the guard page bit, and return a guard page violation */
202 TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE;
203 ASSERT(TempPte.u.Long != 0);
204 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
205 return STATUS_GUARD_PAGE_VIOLATION;
206 }
207
208 /* Nothing to do */
209 return STATUS_SUCCESS;
210 }
211
212 PMMPTE
213 NTAPI
214 MiCheckVirtualAddress(IN PVOID VirtualAddress,
215 OUT PULONG ProtectCode,
216 OUT PMMVAD *ProtoVad)
217 {
218 PMMVAD Vad;
219 PMMPTE PointerPte;
220
221 /* No prototype/section support for now */
222 *ProtoVad = NULL;
223
224 /* User or kernel fault? */
225 if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS)
226 {
227 /* Special case for shared data */
228 if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
229 {
230 /* It's a read-only page */
231 *ProtectCode = MM_READONLY;
232 return MmSharedUserDataPte;
233 }
234
235 /* Find the VAD, it might not exist if the address is bogus */
236 Vad = MiLocateAddress(VirtualAddress);
237 if (!Vad)
238 {
239 /* Bogus virtual address */
240 *ProtectCode = MM_NOACCESS;
241 return NULL;
242 }
243
244 /* ReactOS does not handle physical memory VADs yet */
245 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
246
247 /* Check if it's a section, or just an allocation */
248 if (Vad->u.VadFlags.PrivateMemory)
249 {
250 /* ReactOS does not handle AWE VADs yet */
251 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
252
253 /* This must be a TEB/PEB VAD */
254 if (Vad->u.VadFlags.MemCommit)
255 {
256 /* It's committed, so return the VAD protection */
257 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
258 }
259 else
260 {
261 /* It has not yet been committed, so return no access */
262 *ProtectCode = MM_NOACCESS;
263 }
264
265 /* In both cases, return no PTE */
266 return NULL;
267 }
268 else
269 {
270 /* ReactOS does not supoprt these VADs yet */
271 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
272 ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
273
274 /* Return the proto VAD */
275 *ProtoVad = Vad;
276
277 /* Get the prototype PTE for this page */
278 PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
279 ASSERT(PointerPte != NULL);
280 ASSERT(PointerPte <= Vad->LastContiguousPte);
281
282 /* Return the Prototype PTE and the protection for the page mapping */
283 *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
284 return PointerPte;
285 }
286 }
287 else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
288 {
289 /* This should never happen, as these addresses are handled by the double-maping */
290 if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
291 ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
292 {
293 /* Fail such access */
294 *ProtectCode = MM_NOACCESS;
295 return NULL;
296 }
297
298 /* Return full access rights */
299 *ProtectCode = MM_READWRITE;
300 return NULL;
301 }
302 else if (MI_IS_SESSION_ADDRESS(VirtualAddress))
303 {
304 /* ReactOS does not have an image list yet, so bail out to failure case */
305 ASSERT(IsListEmpty(&MmSessionSpace->ImageList));
306 }
307
308 /* Default case -- failure */
309 *ProtectCode = MM_NOACCESS;
310 return NULL;
311 }
312
313 #if (_MI_PAGING_LEVELS == 2)
314 FORCEINLINE
315 BOOLEAN
316 MiSynchronizeSystemPde(PMMPDE PointerPde)
317 {
318 MMPDE SystemPde;
319 ULONG Index;
320
321 /* Get the Index from the PDE */
322 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
323
324 /* Copy the PDE from the double-mapped system page directory */
325 SystemPde = MmSystemPagePtes[Index];
326 *PointerPde = SystemPde;
327
328 /* Make sure we re-read the PDE and PTE */
329 KeMemoryBarrierWithoutFence();
330
331 /* Return, if we had success */
332 return (BOOLEAN)SystemPde.u.Hard.Valid;
333 }
334
335 NTSTATUS
336 FASTCALL
337 MiCheckPdeForSessionSpace(IN PVOID Address)
338 {
339 MMPTE TempPde;
340 PMMPDE PointerPde;
341 PVOID SessionAddress;
342 ULONG Index;
343
344 /* Is this a session PTE? */
345 if (MI_IS_SESSION_PTE(Address))
346 {
347 /* Make sure the PDE for session space is valid */
348 PointerPde = MiAddressToPde(MmSessionSpace);
349 if (!PointerPde->u.Hard.Valid)
350 {
351 /* This means there's no valid session, bail out */
352 DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
353 Address);
354 DbgBreakPoint();
355 return STATUS_ACCESS_VIOLATION;
356 }
357
358 /* Now get the session-specific page table for this address */
359 SessionAddress = MiPteToAddress(Address);
360 PointerPde = MiAddressToPte(Address);
361 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
362
363 /* It's not valid, so find it in the page table array */
364 Index = ((ULONG_PTR)SessionAddress - (ULONG_PTR)MmSessionBase) >> 22;
365 TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
366 if (TempPde.u.Hard.Valid)
367 {
368 /* The copy is valid, so swap it in */
369 InterlockedExchange((PLONG)PointerPde, TempPde.u.Long);
370 return STATUS_WAIT_1;
371 }
372
373 /* We don't seem to have allocated a page table for this address yet? */
374 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
375 PointerPde->u.Long, SessionAddress);
376 DbgBreakPoint();
377 return STATUS_ACCESS_VIOLATION;
378 }
379
380 /* Is the address also a session address? If not, we're done */
381 if (!MI_IS_SESSION_ADDRESS(Address)) return STATUS_SUCCESS;
382
383 /* It is, so again get the PDE for session space */
384 PointerPde = MiAddressToPde(MmSessionSpace);
385 if (!PointerPde->u.Hard.Valid)
386 {
387 /* This means there's no valid session, bail out */
388 DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
389 Address);
390 DbgBreakPoint();
391 return STATUS_ACCESS_VIOLATION;
392 }
393
394 /* Now get the PDE for the address itself */
395 PointerPde = MiAddressToPde(Address);
396 if (!PointerPde->u.Hard.Valid)
397 {
398 /* Do the swap, we should be good to go */
399 Index = ((ULONG_PTR)Address - (ULONG_PTR)MmSessionBase) >> 22;
400 PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
401 if (PointerPde->u.Hard.Valid) return STATUS_WAIT_1;
402
403 /* We had not allocated a page table for this session address yet, fail! */
404 DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
405 PointerPde->u.Long, Address);
406 DbgBreakPoint();
407 return STATUS_ACCESS_VIOLATION;
408 }
409
410 /* It's valid, so there's nothing to do */
411 return STATUS_SUCCESS;
412 }
413
414 NTSTATUS
415 FASTCALL
416 MiCheckPdeForPagedPool(IN PVOID Address)
417 {
418 PMMPDE PointerPde;
419 NTSTATUS Status = STATUS_SUCCESS;
420
421 /* Check session PDE */
422 if (MI_IS_SESSION_ADDRESS(Address)) return MiCheckPdeForSessionSpace(Address);
423 if (MI_IS_SESSION_PTE(Address)) return MiCheckPdeForSessionSpace(Address);
424
425 //
426 // Check if this is a fault while trying to access the page table itself
427 //
428 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
429 {
430 //
431 // Send a hint to the page fault handler that this is only a valid fault
432 // if we already detected this was access within the page table range
433 //
434 PointerPde = (PMMPDE)MiAddressToPte(Address);
435 Status = STATUS_WAIT_1;
436 }
437 else if (Address < MmSystemRangeStart)
438 {
439 //
440 // This is totally illegal
441 //
442 return STATUS_ACCESS_VIOLATION;
443 }
444 else
445 {
446 //
447 // Get the PDE for the address
448 //
449 PointerPde = MiAddressToPde(Address);
450 }
451
452 //
453 // Check if it's not valid
454 //
455 if (PointerPde->u.Hard.Valid == 0)
456 {
457 //
458 // Copy it from our double-mapped system page directory
459 //
460 InterlockedExchangePte(PointerPde,
461 MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
462 }
463
464 //
465 // Return status
466 //
467 return Status;
468 }
469 #else
470 NTSTATUS
471 FASTCALL
472 MiCheckPdeForPagedPool(IN PVOID Address)
473 {
474 return STATUS_ACCESS_VIOLATION;
475 }
476 #endif
477
478 VOID
479 NTAPI
480 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
481 {
482 PMMPTE ZeroPte;
483 MMPTE TempPte;
484 PMMPFN Pfn1;
485 PVOID ZeroAddress;
486
487 /* Get the PFN for this page */
488 Pfn1 = MiGetPfnEntry(PageFrameNumber);
489 ASSERT(Pfn1);
490
491 /* Grab a system PTE we can use to zero the page */
492 ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
493 ASSERT(ZeroPte);
494
495 /* Initialize the PTE for it */
496 TempPte = ValidKernelPte;
497 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
498
499 /* Setup caching */
500 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
501 {
502 /* Write combining, no caching */
503 MI_PAGE_DISABLE_CACHE(&TempPte);
504 MI_PAGE_WRITE_COMBINED(&TempPte);
505 }
506 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
507 {
508 /* Write through, no caching */
509 MI_PAGE_DISABLE_CACHE(&TempPte);
510 MI_PAGE_WRITE_THROUGH(&TempPte);
511 }
512
513 /* Make the system PTE valid with our PFN */
514 MI_WRITE_VALID_PTE(ZeroPte, TempPte);
515
516 /* Get the address it maps to, and zero it out */
517 ZeroAddress = MiPteToAddress(ZeroPte);
518 KeZeroPages(ZeroAddress, PAGE_SIZE);
519
520 /* Now get rid of it */
521 MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
522 }
523
524 VOID
525 NTAPI
526 MiCopyPfn(
527 _In_ PFN_NUMBER DestPage,
528 _In_ PFN_NUMBER SrcPage)
529 {
530 PMMPTE SysPtes;
531 MMPTE TempPte;
532 PMMPFN DestPfn, SrcPfn;
533 PVOID DestAddress;
534 const VOID* SrcAddress;
535
536 /* Get the PFNs */
537 DestPfn = MiGetPfnEntry(DestPage);
538 ASSERT(DestPfn);
539 SrcPfn = MiGetPfnEntry(SrcPage);
540 ASSERT(SrcPfn);
541
542 /* Grab 2 system PTEs */
543 SysPtes = MiReserveSystemPtes(2, SystemPteSpace);
544 ASSERT(SysPtes);
545
546 /* Initialize the destination PTE */
547 TempPte = ValidKernelPte;
548 TempPte.u.Hard.PageFrameNumber = DestPage;
549
550 /* Setup caching */
551 if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined)
552 {
553 /* Write combining, no caching */
554 MI_PAGE_DISABLE_CACHE(&TempPte);
555 MI_PAGE_WRITE_COMBINED(&TempPte);
556 }
557 else if (DestPfn->u3.e1.CacheAttribute == MiNonCached)
558 {
559 /* Write through, no caching */
560 MI_PAGE_DISABLE_CACHE(&TempPte);
561 MI_PAGE_WRITE_THROUGH(&TempPte);
562 }
563
564 /* Make the system PTE valid with our PFN */
565 MI_WRITE_VALID_PTE(&SysPtes[0], TempPte);
566
567 /* Initialize the source PTE */
568 TempPte = ValidKernelPte;
569 TempPte.u.Hard.PageFrameNumber = SrcPage;
570
571 /* Setup caching */
572 if (SrcPfn->u3.e1.CacheAttribute == MiNonCached)
573 {
574 MI_PAGE_DISABLE_CACHE(&TempPte);
575 }
576
577 /* Make the system PTE valid with our PFN */
578 MI_WRITE_VALID_PTE(&SysPtes[1], TempPte);
579
580 /* Get the addresses and perform the copy */
581 DestAddress = MiPteToAddress(&SysPtes[0]);
582 SrcAddress = MiPteToAddress(&SysPtes[1]);
583 RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
584
585 /* Now get rid of it */
586 MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace);
587 }
588
589 NTSTATUS
590 NTAPI
591 MiResolveDemandZeroFault(IN PVOID Address,
592 IN PMMPTE PointerPte,
593 IN PEPROCESS Process,
594 IN KIRQL OldIrql)
595 {
596 PFN_NUMBER PageFrameNumber = 0;
597 MMPTE TempPte;
598 BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
599 ULONG Color;
600 PMMPFN Pfn1;
601 DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
602 Address,
603 Process);
604
605 /* Must currently only be called by paging path */
606 if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL))
607 {
608 /* Sanity check */
609 ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
610
611 /* No forking yet */
612 ASSERT(Process->ForkInProgress == NULL);
613
614 /* Get process color */
615 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
616 ASSERT(Color != 0xFFFFFFFF);
617
618 /* We'll need a zero page */
619 NeedZero = TRUE;
620 }
621 else
622 {
623 /* Check if we need a zero page */
624 NeedZero = (OldIrql != MM_NOIRQL);
625
626 /* Session-backed image views must be zeroed */
627 if ((Process == HYDRA_PROCESS) &&
628 ((MI_IS_SESSION_IMAGE_ADDRESS(Address)) ||
629 ((Address >= MiSessionViewStart) && (Address < MiSessionSpaceWs))))
630 {
631 NeedZero = TRUE;
632 }
633
634 /* Hardcode unknown color */
635 Color = 0xFFFFFFFF;
636 }
637
638 /* Check if the PFN database should be acquired */
639 if (OldIrql == MM_NOIRQL)
640 {
641 /* Acquire it and remember we should release it after */
642 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
643 HaveLock = TRUE;
644 }
645
646 /* We either manually locked the PFN DB, or already came with it locked */
647 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
648 ASSERT(PointerPte->u.Hard.Valid == 0);
649
650 /* Assert we have enough pages */
651 ASSERT(MmAvailablePages >= 32);
652
653 #if MI_TRACE_PFNS
654 if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
655 if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
656 #endif
657 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
658 if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
659
660 /* Do we need a zero page? */
661 if (Color != 0xFFFFFFFF)
662 {
663 /* Try to get one, if we couldn't grab a free page and zero it */
664 PageFrameNumber = MiRemoveZeroPageSafe(Color);
665 if (!PageFrameNumber)
666 {
667 /* We'll need a free page and zero it manually */
668 PageFrameNumber = MiRemoveAnyPage(Color);
669 NeedZero = TRUE;
670 }
671 }
672 else
673 {
674 /* Get a color, and see if we should grab a zero or non-zero page */
675 Color = MI_GET_NEXT_COLOR();
676 if (!NeedZero)
677 {
678 /* Process or system doesn't want a zero page, grab anything */
679 PageFrameNumber = MiRemoveAnyPage(Color);
680 }
681 else
682 {
683 /* System wants a zero page, obtain one */
684 PageFrameNumber = MiRemoveZeroPage(Color);
685 }
686 }
687
688 /* Initialize it */
689 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
690
691 /* Do we have the lock? */
692 if (HaveLock)
693 {
694 /* Release it */
695 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
696
697 /* Update performance counters */
698 if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
699 }
700
701 /* Increment demand zero faults */
702 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
703
704 /* Zero the page if need be */
705 if (NeedZero) MiZeroPfn(PageFrameNumber);
706
707 /* Fault on user PDE, or fault on user PTE? */
708 if (PointerPte <= MiHighestUserPte)
709 {
710 /* User fault, build a user PTE */
711 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
712 PointerPte,
713 PointerPte->u.Soft.Protection,
714 PageFrameNumber);
715 }
716 else
717 {
718 /* This is a user-mode PDE, create a kernel PTE for it */
719 MI_MAKE_HARDWARE_PTE(&TempPte,
720 PointerPte,
721 PointerPte->u.Soft.Protection,
722 PageFrameNumber);
723 }
724
725 /* Set it dirty if it's a writable page */
726 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
727
728 /* Write it */
729 MI_WRITE_VALID_PTE(PointerPte, TempPte);
730
731 /* Did we manually acquire the lock */
732 if (HaveLock)
733 {
734 /* Get the PFN entry */
735 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
736
737 /* Windows does these sanity checks */
738 ASSERT(Pfn1->u1.Event == 0);
739 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
740 }
741
742 //
743 // It's all good now
744 //
745 DPRINT("Demand zero page has now been paged in\n");
746 return STATUS_PAGE_FAULT_DEMAND_ZERO;
747 }
748
749 NTSTATUS
750 NTAPI
751 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
752 IN PVOID Address,
753 IN PMMPTE PointerPte,
754 IN PMMPTE PointerProtoPte,
755 IN KIRQL OldIrql,
756 IN PMMPFN* LockedProtoPfn)
757 {
758 MMPTE TempPte;
759 PMMPTE OriginalPte, PageTablePte;
760 ULONG_PTR Protection;
761 PFN_NUMBER PageFrameIndex;
762 PMMPFN Pfn1, Pfn2;
763 BOOLEAN OriginalProtection, DirtyPage;
764
765 /* Must be called with an valid prototype PTE, with the PFN lock held */
766 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
767 ASSERT(PointerProtoPte->u.Hard.Valid == 1);
768
769 /* Get the page */
770 PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
771
772 /* Get the PFN entry and set it as a prototype PTE */
773 Pfn1 = MiGetPfnEntry(PageFrameIndex);
774 Pfn1->u3.e1.PrototypePte = 1;
775
776 /* Increment the share count for the page table */
777 PageTablePte = MiAddressToPte(PointerPte);
778 Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
779 Pfn2->u2.ShareCount++;
780
781 /* Check where we should be getting the protection information from */
782 if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
783 {
784 /* Get the protection from the PTE, there's no real Proto PTE data */
785 Protection = PointerPte->u.Soft.Protection;
786
787 /* Remember that we did not use the proto protection */
788 OriginalProtection = FALSE;
789 }
790 else
791 {
792 /* Get the protection from the original PTE link */
793 OriginalPte = &Pfn1->OriginalPte;
794 Protection = OriginalPte->u.Soft.Protection;
795
796 /* Remember that we used the original protection */
797 OriginalProtection = TRUE;
798
799 /* Check if this was a write on a read only proto */
800 if ((StoreInstruction) && !(Protection & MM_READWRITE))
801 {
802 /* Clear the flag */
803 StoreInstruction = 0;
804 }
805 }
806
807 /* Check if this was a write on a non-COW page */
808 DirtyPage = FALSE;
809 if ((StoreInstruction) && ((Protection & MM_WRITECOPY) != MM_WRITECOPY))
810 {
811 /* Then the page should be marked dirty */
812 DirtyPage = TRUE;
813
814 /* ReactOS check */
815 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype != 0);
816 }
817
818 /* Did we get a locked incoming PFN? */
819 if (*LockedProtoPfn)
820 {
821 /* Drop a reference */
822 ASSERT((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
823 MiDereferencePfnAndDropLockCount(*LockedProtoPfn);
824 *LockedProtoPfn = NULL;
825 }
826
827 /* Release the PFN lock */
828 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
829
830 /* Remove special/caching bits */
831 Protection &= ~MM_PROTECT_SPECIAL;
832
833 /* Setup caching */
834 if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
835 {
836 /* Write combining, no caching */
837 MI_PAGE_DISABLE_CACHE(&TempPte);
838 MI_PAGE_WRITE_COMBINED(&TempPte);
839 }
840 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
841 {
842 /* Write through, no caching */
843 MI_PAGE_DISABLE_CACHE(&TempPte);
844 MI_PAGE_WRITE_THROUGH(&TempPte);
845 }
846
847 /* Check if this is a kernel or user address */
848 if (Address < MmSystemRangeStart)
849 {
850 /* Build the user PTE */
851 MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
852 }
853 else
854 {
855 /* Build the kernel PTE */
856 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
857 }
858
859 /* Set the dirty flag if needed */
860 if (DirtyPage) MI_MAKE_DIRTY_PAGE(&TempPte);
861
862 /* Write the PTE */
863 MI_WRITE_VALID_PTE(PointerPte, TempPte);
864
865 /* Reset the protection if needed */
866 if (OriginalProtection) Protection = MM_ZERO_ACCESS;
867
868 /* Return success */
869 ASSERT(PointerPte == MiAddressToPte(Address));
870 return STATUS_SUCCESS;
871 }
872
873 NTSTATUS
874 NTAPI
875 MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
876 _In_ PVOID FaultingAddress,
877 _In_ PMMPTE PointerPte,
878 _In_ PEPROCESS CurrentProcess,
879 _Inout_ KIRQL *OldIrql)
880 {
881 ULONG Color;
882 PFN_NUMBER Page;
883 NTSTATUS Status;
884 MMPTE TempPte = *PointerPte;
885 KEVENT Event;
886 PMMPFN Pfn1;
887 ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
888 ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
889
890 /* Things we don't support yet */
891 ASSERT(CurrentProcess > HYDRA_PROCESS);
892 ASSERT(*OldIrql != MM_NOIRQL);
893
894 /* We must hold the PFN lock */
895 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
896
897 /* Some sanity checks */
898 ASSERT(TempPte.u.Hard.Valid == 0);
899 ASSERT(TempPte.u.Soft.PageFileHigh != 0);
900 ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
901
902 /* Get any page, it will be overwritten */
903 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
904 Page = MiRemoveAnyPage(Color);
905
906 /* Initialize this PFN */
907 MiInitializePfn(Page, PointerPte, StoreInstruction);
908
909 /* Sets the PFN as being in IO operation */
910 Pfn1 = MI_PFN_ELEMENT(Page);
911 ASSERT(Pfn1->u1.Event == NULL);
912 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
913 ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
914
915 KeInitializeEvent(&Event, NotificationEvent, FALSE);
916 Pfn1->u1.Event = &Event;
917 Pfn1->u3.e1.ReadInProgress = 1;
918
919 /* We must write the PTE now as the PFN lock will be released while performing the IO operation */
920 TempPte.u.Soft.Transition = 1;
921 TempPte.u.Soft.PageFileLow = 0;
922 TempPte.u.Soft.Prototype = 0;
923 TempPte.u.Trans.PageFrameNumber = Page;
924
925 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
926
927 /* Release the PFN lock while we proceed */
928 KeReleaseQueuedSpinLock(LockQueuePfnLock, *OldIrql);
929
930 /* Do the paging IO */
931 Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset);
932
933 /* Lock the PFN database again */
934 *OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
935
936 /* Nobody should have changed that while we were not looking */
937 ASSERT(Pfn1->u1.Event == &Event);
938 ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
939 ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
940
941 if (!NT_SUCCESS(Status))
942 {
943 /* Malheur! */
944 ASSERT(FALSE);
945 Pfn1->u4.InPageError = 1;
946 Pfn1->u1.ReadStatus = Status;
947 }
948
949 /* This is now a nice and normal PFN */
950 Pfn1->u1.Event = NULL;
951 Pfn1->u3.e1.ReadInProgress = 0;
952
953 /* And the PTE can finally be valid */
954 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, TempPte.u.Trans.Protection, Page);
955 MI_WRITE_VALID_PTE(PointerPte, TempPte);
956
957 /* Waiters gonna wait */
958 KeSetEvent(&Event, IO_NO_INCREMENT, FALSE);
959
960 return Status;
961 }
962
963 NTSTATUS
964 NTAPI
965 MiResolveTransitionFault(IN PVOID FaultingAddress,
966 IN PMMPTE PointerPte,
967 IN PEPROCESS CurrentProcess,
968 IN KIRQL OldIrql,
969 OUT PVOID *InPageBlock)
970 {
971 PFN_NUMBER PageFrameIndex;
972 PMMPFN Pfn1;
973 MMPTE TempPte;
974 PMMPTE PointerToPteForProtoPage;
975 DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
976 FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
977
978 /* Windowss does this check */
979 ASSERT(*InPageBlock == NULL);
980
981 /* ARM3 doesn't support this path */
982 ASSERT(OldIrql != MM_NOIRQL);
983
984 /* Capture the PTE and make sure it's in transition format */
985 TempPte = *PointerPte;
986 ASSERT((TempPte.u.Soft.Valid == 0) &&
987 (TempPte.u.Soft.Prototype == 0) &&
988 (TempPte.u.Soft.Transition == 1));
989
990 /* Get the PFN and the PFN entry */
991 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
992 DPRINT("Transition PFN: %lx\n", PageFrameIndex);
993 Pfn1 = MiGetPfnEntry(PageFrameIndex);
994
995 /* One more transition fault! */
996 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
997
998 /* This is from ARM3 -- Windows normally handles this here */
999 ASSERT(Pfn1->u4.InPageError == 0);
1000
1001 /* See if we should wait before terminating the fault */
1002 if (Pfn1->u3.e1.ReadInProgress == 1)
1003 {
1004 DPRINT1("The page is currently being read!\n");
1005 ASSERT(Pfn1->u1.Event != NULL);
1006 *InPageBlock = Pfn1->u1.Event;
1007 if (PointerPte == Pfn1->PteAddress)
1008 {
1009 DPRINT1("And this if for this particular PTE.\n");
1010 /* The PTE will be made valid by the thread serving the fault */
1011 return STATUS_SUCCESS; // FIXME: Maybe something more descriptive
1012 }
1013 }
1014
1015 /* Windows checks there's some free pages and this isn't an in-page error */
1016 ASSERT(MmAvailablePages > 0);
1017 ASSERT(Pfn1->u4.InPageError == 0);
1018
1019 /* ReactOS checks for this */
1020 ASSERT(MmAvailablePages > 32);
1021
1022 /* Was this a transition page in the valid list, or free/zero list? */
1023 if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
1024 {
1025 /* All Windows does here is a bunch of sanity checks */
1026 DPRINT("Transition in active list\n");
1027 ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
1028 (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
1029 ASSERT(Pfn1->u2.ShareCount != 0);
1030 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
1031 }
1032 else
1033 {
1034 /* Otherwise, the page is removed from its list */
1035 DPRINT("Transition page in free/zero list\n");
1036 MiUnlinkPageFromList(Pfn1);
1037 MiReferenceUnusedPageAndBumpLockCount(Pfn1);
1038 }
1039
1040 /* At this point, there should no longer be any in-page errors */
1041 ASSERT(Pfn1->u4.InPageError == 0);
1042
1043 /* Check if this was a PFN with no more share references */
1044 if (Pfn1->u2.ShareCount == 0) MiDropLockCount(Pfn1);
1045
1046 /* Bump the share count and make the page valid */
1047 Pfn1->u2.ShareCount++;
1048 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1049
1050 /* Prototype PTEs are in paged pool, which itself might be in transition */
1051 if (FaultingAddress >= MmSystemRangeStart)
1052 {
1053 /* Check if this is a paged pool PTE in transition state */
1054 PointerToPteForProtoPage = MiAddressToPte(PointerPte);
1055 TempPte = *PointerToPteForProtoPage;
1056 if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
1057 {
1058 /* This isn't yet supported */
1059 DPRINT1("Double transition fault not yet supported\n");
1060 ASSERT(FALSE);
1061 }
1062 }
1063
1064 /* Build the transition PTE -- maybe a macro? */
1065 ASSERT(PointerPte->u.Hard.Valid == 0);
1066 ASSERT(PointerPte->u.Trans.Prototype == 0);
1067 ASSERT(PointerPte->u.Trans.Transition == 1);
1068 TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
1069 (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
1070 MiDetermineUserGlobalPteMask(PointerPte);
1071
1072 /* Is the PTE writeable? */
1073 if ((Pfn1->u3.e1.Modified) &&
1074 MI_IS_PAGE_WRITEABLE(&TempPte) &&
1075 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1076 {
1077 /* Make it dirty */
1078 MI_MAKE_DIRTY_PAGE(&TempPte);
1079 }
1080 else
1081 {
1082 /* Make it clean */
1083 MI_MAKE_CLEAN_PAGE(&TempPte);
1084 }
1085
1086 /* Write the valid PTE */
1087 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1088
1089 /* Return success */
1090 return STATUS_PAGE_FAULT_TRANSITION;
1091 }
1092
1093 NTSTATUS
1094 NTAPI
1095 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
1096 IN PVOID Address,
1097 IN PMMPTE PointerPte,
1098 IN PMMPTE PointerProtoPte,
1099 IN OUT PMMPFN *OutPfn,
1100 OUT PVOID *PageFileData,
1101 OUT PMMPTE PteValue,
1102 IN PEPROCESS Process,
1103 IN KIRQL OldIrql,
1104 IN PVOID TrapInformation)
1105 {
1106 MMPTE TempPte, PteContents;
1107 PMMPFN Pfn1;
1108 PFN_NUMBER PageFrameIndex;
1109 NTSTATUS Status;
1110 PVOID InPageBlock = NULL;
1111 ULONG Protection;
1112
1113 /* Must be called with an invalid, prototype PTE, with the PFN lock held */
1114 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1115 ASSERT(PointerPte->u.Hard.Valid == 0);
1116 ASSERT(PointerPte->u.Soft.Prototype == 1);
1117
1118 /* Read the prototype PTE and check if it's valid */
1119 TempPte = *PointerProtoPte;
1120 if (TempPte.u.Hard.Valid == 1)
1121 {
1122 /* One more user of this mapped page */
1123 PageFrameIndex = PFN_FROM_PTE(&TempPte);
1124 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1125 Pfn1->u2.ShareCount++;
1126
1127 /* Call it a transition */
1128 InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
1129
1130 /* Complete the prototype PTE fault -- this will release the PFN lock */
1131 return MiCompleteProtoPteFault(StoreInstruction,
1132 Address,
1133 PointerPte,
1134 PointerProtoPte,
1135 OldIrql,
1136 OutPfn);
1137 }
1138
1139 /* Make sure there's some protection mask */
1140 if (TempPte.u.Long == 0)
1141 {
1142 /* Release the lock */
1143 DPRINT1("Access on reserved section?\n");
1144 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1145 return STATUS_ACCESS_VIOLATION;
1146 }
1147
1148 /* There is no such thing as a decommitted prototype PTE */
1149 ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
1150
1151 /* Check for access rights on the PTE proper */
1152 PteContents = *PointerPte;
1153 if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
1154 {
1155 if (!PteContents.u.Proto.ReadOnly)
1156 {
1157 Protection = TempPte.u.Soft.Protection;
1158 }
1159 else
1160 {
1161 Protection = MM_READONLY;
1162 }
1163 /* Check for page acess in software */
1164 Status = MiAccessCheck(PointerProtoPte,
1165 StoreInstruction,
1166 KernelMode,
1167 TempPte.u.Soft.Protection,
1168 TrapInformation,
1169 TRUE);
1170 ASSERT(Status == STATUS_SUCCESS);
1171 }
1172 else
1173 {
1174 Protection = PteContents.u.Soft.Protection;
1175 }
1176
1177 /* Check for writing copy on write page */
1178 if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
1179 {
1180 PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
1181 ULONG Color;
1182
1183 /* Resolve the proto fault as if it was a read operation */
1184 Status = MiResolveProtoPteFault(FALSE,
1185 Address,
1186 PointerPte,
1187 PointerProtoPte,
1188 OutPfn,
1189 PageFileData,
1190 PteValue,
1191 Process,
1192 OldIrql,
1193 TrapInformation);
1194
1195 if (!NT_SUCCESS(Status))
1196 {
1197 return Status;
1198 }
1199
1200 /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
1201 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1202
1203 /* And re-read the proto PTE */
1204 TempPte = *PointerProtoPte;
1205 ASSERT(TempPte.u.Hard.Valid == 1);
1206 ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
1207
1208 /* Get a new page for the private copy */
1209 if (Process > HYDRA_PROCESS)
1210 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1211 else
1212 Color = MI_GET_NEXT_COLOR();
1213
1214 PageFrameIndex = MiRemoveAnyPage(Color);
1215
1216 /* Perform the copy */
1217 MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
1218
1219 /* This will drop everything MiResolveProtoPteFault referenced */
1220 MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
1221
1222 /* Because now we use this */
1223 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1224 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
1225
1226 /* Fix the protection */
1227 Protection &= ~MM_WRITECOPY;
1228 Protection |= MM_READWRITE;
1229 if (Address < MmSystemRangeStart)
1230 {
1231 /* Build the user PTE */
1232 MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex);
1233 }
1234 else
1235 {
1236 /* Build the kernel PTE */
1237 MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex);
1238 }
1239
1240 /* And finally, write the valid PTE */
1241 MI_WRITE_VALID_PTE(PointerPte, PteContents);
1242
1243 /* The caller expects us to release the PFN lock */
1244 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1245 return Status;
1246 }
1247
1248 /* Check for clone PTEs */
1249 if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
1250
1251 /* We don't support mapped files yet */
1252 ASSERT(TempPte.u.Soft.Prototype == 0);
1253
1254 /* We might however have transition PTEs */
1255 if (TempPte.u.Soft.Transition == 1)
1256 {
1257 /* Resolve the transition fault */
1258 ASSERT(OldIrql != MM_NOIRQL);
1259 Status = MiResolveTransitionFault(Address,
1260 PointerProtoPte,
1261 Process,
1262 OldIrql,
1263 &InPageBlock);
1264 ASSERT(NT_SUCCESS(Status));
1265 }
1266 else
1267 {
1268 /* We also don't support paged out pages */
1269 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1270
1271 /* Resolve the demand zero fault */
1272 Status = MiResolveDemandZeroFault(Address,
1273 PointerProtoPte,
1274 Process,
1275 OldIrql);
1276 ASSERT(NT_SUCCESS(Status));
1277 }
1278
1279 /* Complete the prototype PTE fault -- this will release the PFN lock */
1280 ASSERT(PointerPte->u.Hard.Valid == 0);
1281 return MiCompleteProtoPteFault(StoreInstruction,
1282 Address,
1283 PointerPte,
1284 PointerProtoPte,
1285 OldIrql,
1286 OutPfn);
1287 }
1288
1289 NTSTATUS
1290 NTAPI
1291 MiDispatchFault(IN BOOLEAN StoreInstruction,
1292 IN PVOID Address,
1293 IN PMMPTE PointerPte,
1294 IN PMMPTE PointerProtoPte,
1295 IN BOOLEAN Recursive,
1296 IN PEPROCESS Process,
1297 IN PVOID TrapInformation,
1298 IN PMMVAD Vad)
1299 {
1300 MMPTE TempPte;
1301 KIRQL OldIrql, LockIrql;
1302 NTSTATUS Status;
1303 PMMPTE SuperProtoPte;
1304 PMMPFN Pfn1, OutPfn = NULL;
1305 PFN_NUMBER PageFrameIndex;
1306 PFN_COUNT PteCount, ProcessedPtes;
1307 DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
1308 Address,
1309 Process);
1310
1311 /* Make sure the addresses are ok */
1312 ASSERT(PointerPte == MiAddressToPte(Address));
1313
1314 //
1315 // Make sure APCs are off and we're not at dispatch
1316 //
1317 OldIrql = KeGetCurrentIrql();
1318 ASSERT(OldIrql <= APC_LEVEL);
1319 ASSERT(KeAreAllApcsDisabled() == TRUE);
1320
1321 //
1322 // Grab a copy of the PTE
1323 //
1324 TempPte = *PointerPte;
1325
1326 /* Do we have a prototype PTE? */
1327 if (PointerProtoPte)
1328 {
1329 /* This should never happen */
1330 ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
1331
1332 /* Check if this is a kernel-mode address */
1333 SuperProtoPte = MiAddressToPte(PointerProtoPte);
1334 if (Address >= MmSystemRangeStart)
1335 {
1336 /* Lock the PFN database */
1337 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1338
1339 /* Has the PTE been made valid yet? */
1340 if (!SuperProtoPte->u.Hard.Valid)
1341 {
1342 ASSERT(FALSE);
1343 }
1344 else if (PointerPte->u.Hard.Valid == 1)
1345 {
1346 ASSERT(FALSE);
1347 }
1348
1349 /* Resolve the fault -- this will release the PFN lock */
1350 Status = MiResolveProtoPteFault(StoreInstruction,
1351 Address,
1352 PointerPte,
1353 PointerProtoPte,
1354 &OutPfn,
1355 NULL,
1356 NULL,
1357 Process,
1358 LockIrql,
1359 TrapInformation);
1360 ASSERT(Status == STATUS_SUCCESS);
1361
1362 /* Complete this as a transition fault */
1363 ASSERT(OldIrql == KeGetCurrentIrql());
1364 ASSERT(OldIrql <= APC_LEVEL);
1365 ASSERT(KeAreAllApcsDisabled() == TRUE);
1366 return Status;
1367 }
1368 else
1369 {
1370 /* We only handle the lookup path */
1371 ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
1372
1373 /* Is there a non-image VAD? */
1374 if ((Vad) &&
1375 (Vad->u.VadFlags.VadType != VadImageMap) &&
1376 !(Vad->u2.VadFlags2.ExtendableFile))
1377 {
1378 /* One day, ReactOS will cluster faults */
1379 ASSERT(Address <= MM_HIGHEST_USER_ADDRESS);
1380 DPRINT("Should cluster fault, but won't\n");
1381 }
1382
1383 /* Only one PTE to handle for now */
1384 PteCount = 1;
1385 ProcessedPtes = 0;
1386
1387 /* Lock the PFN database */
1388 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1389
1390 /* We only handle the valid path */
1391 ASSERT(SuperProtoPte->u.Hard.Valid == 1);
1392
1393 /* Capture the PTE */
1394 TempPte = *PointerProtoPte;
1395
1396 /* Loop to handle future case of clustered faults */
1397 while (TRUE)
1398 {
1399 /* For our current usage, this should be true */
1400 if (TempPte.u.Hard.Valid == 1)
1401 {
1402 /* Bump the share count on the PTE */
1403 PageFrameIndex = PFN_FROM_PTE(&TempPte);
1404 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1405 Pfn1->u2.ShareCount++;
1406 }
1407 else if ((TempPte.u.Soft.Prototype == 0) &&
1408 (TempPte.u.Soft.Transition == 1))
1409 {
1410 /* This is a standby page, bring it back from the cache */
1411 PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
1412 DPRINT("oooh, shiny, a soft fault! 0x%lx\n", PageFrameIndex);
1413 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1414 ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1415
1416 /* Should not yet happen in ReactOS */
1417 ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
1418 ASSERT(Pfn1->u4.InPageError == 0);
1419
1420 /* Get the page */
1421 MiUnlinkPageFromList(Pfn1);
1422
1423 /* Bump its reference count */
1424 ASSERT(Pfn1->u2.ShareCount == 0);
1425 InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1426 Pfn1->u2.ShareCount++;
1427
1428 /* Make it valid again */
1429 /* This looks like another macro.... */
1430 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1431 ASSERT(PointerProtoPte->u.Hard.Valid == 0);
1432 ASSERT(PointerProtoPte->u.Trans.Prototype == 0);
1433 ASSERT(PointerProtoPte->u.Trans.Transition == 1);
1434 TempPte.u.Long = (PointerProtoPte->u.Long & ~0xFFF) |
1435 MmProtectToPteMask[PointerProtoPte->u.Trans.Protection];
1436 TempPte.u.Hard.Valid = 1;
1437 MI_MAKE_ACCESSED_PAGE(&TempPte);
1438
1439 /* Is the PTE writeable? */
1440 if ((Pfn1->u3.e1.Modified) &&
1441 MI_IS_PAGE_WRITEABLE(&TempPte) &&
1442 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1443 {
1444 /* Make it dirty */
1445 MI_MAKE_DIRTY_PAGE(&TempPte);
1446 }
1447 else
1448 {
1449 /* Make it clean */
1450 MI_MAKE_CLEAN_PAGE(&TempPte);
1451 }
1452
1453 /* Write the valid PTE */
1454 MI_WRITE_VALID_PTE(PointerProtoPte, TempPte);
1455 ASSERT(PointerPte->u.Hard.Valid == 0);
1456 }
1457 else
1458 {
1459 /* Page is invalid, get out of the loop */
1460 break;
1461 }
1462
1463 /* One more done, was it the last? */
1464 if (++ProcessedPtes == PteCount)
1465 {
1466 /* Complete the fault */
1467 MiCompleteProtoPteFault(StoreInstruction,
1468 Address,
1469 PointerPte,
1470 PointerProtoPte,
1471 LockIrql,
1472 &OutPfn);
1473
1474 /* THIS RELEASES THE PFN LOCK! */
1475 break;
1476 }
1477
1478 /* No clustered faults yet */
1479 ASSERT(FALSE);
1480 }
1481
1482 /* Did we resolve the fault? */
1483 if (ProcessedPtes)
1484 {
1485 /* Bump the transition count */
1486 InterlockedExchangeAddSizeT(&KeGetCurrentPrcb()->MmTransitionCount, ProcessedPtes);
1487 ProcessedPtes--;
1488
1489 /* Loop all the processing we did */
1490 ASSERT(ProcessedPtes == 0);
1491
1492 /* Complete this as a transition fault */
1493 ASSERT(OldIrql == KeGetCurrentIrql());
1494 ASSERT(OldIrql <= APC_LEVEL);
1495 ASSERT(KeAreAllApcsDisabled() == TRUE);
1496 return STATUS_PAGE_FAULT_TRANSITION;
1497 }
1498
1499 /* We did not -- PFN lock is still held, prepare to resolve prototype PTE fault */
1500 OutPfn = MI_PFN_ELEMENT(SuperProtoPte->u.Hard.PageFrameNumber);
1501 MiReferenceUsedPageAndBumpLockCount(OutPfn);
1502 ASSERT(OutPfn->u3.e2.ReferenceCount > 1);
1503 ASSERT(PointerPte->u.Hard.Valid == 0);
1504
1505 /* Resolve the fault -- this will release the PFN lock */
1506 Status = MiResolveProtoPteFault(StoreInstruction,
1507 Address,
1508 PointerPte,
1509 PointerProtoPte,
1510 &OutPfn,
1511 NULL,
1512 NULL,
1513 Process,
1514 LockIrql,
1515 TrapInformation);
1516 //ASSERT(Status != STATUS_ISSUE_PAGING_IO);
1517 //ASSERT(Status != STATUS_REFAULT);
1518 //ASSERT(Status != STATUS_PTE_CHANGED);
1519
1520 /* Did the routine clean out the PFN or should we? */
1521 if (OutPfn)
1522 {
1523 /* We had a locked PFN, so acquire the PFN lock to dereference it */
1524 ASSERT(PointerProtoPte != NULL);
1525 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1526
1527 /* Dereference the locked PFN */
1528 MiDereferencePfnAndDropLockCount(OutPfn);
1529 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
1530
1531 /* And now release the lock */
1532 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1533 }
1534
1535 /* Complete this as a transition fault */
1536 ASSERT(OldIrql == KeGetCurrentIrql());
1537 ASSERT(OldIrql <= APC_LEVEL);
1538 ASSERT(KeAreAllApcsDisabled() == TRUE);
1539 return Status;
1540 }
1541 }
1542
1543 /* Is this a transition PTE */
1544 if (TempPte.u.Soft.Transition)
1545 {
1546 PVOID InPageBlock = NULL;
1547 /* Lock the PFN database */
1548 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1549
1550 /* Resolve */
1551 Status = MiResolveTransitionFault(Address, PointerPte, Process, LockIrql, &InPageBlock);
1552
1553 ASSERT(NT_SUCCESS(Status));
1554
1555 /* And now release the lock and leave*/
1556 KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
1557
1558 if (InPageBlock != NULL)
1559 {
1560 /* The page is being paged in by another process */
1561 KeWaitForSingleObject(InPageBlock, WrPageIn, KernelMode, FALSE, NULL);
1562 }
1563
1564 ASSERT(OldIrql == KeGetCurrentIrql());
1565 ASSERT(OldIrql <= APC_LEVEL);
1566 ASSERT(KeAreAllApcsDisabled() == TRUE);
1567 return Status;
1568 }
1569
1570 /* Should we page the data back in ? */
1571 if (TempPte.u.Soft.PageFileHigh != 0)
1572 {
1573 /* Lock the PFN database */
1574 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1575
1576 /* Resolve */
1577 Status = MiResolvePageFileFault(StoreInstruction, Address, PointerPte, Process, &LockIrql);
1578
1579 /* And now release the lock and leave*/
1580 KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
1581
1582 ASSERT(OldIrql == KeGetCurrentIrql());
1583 ASSERT(OldIrql <= APC_LEVEL);
1584 ASSERT(KeAreAllApcsDisabled() == TRUE);
1585 return Status;
1586 }
1587
1588 //
1589 // The PTE must be invalid but not completely empty. It must also not be a
1590 // prototype a transition or a paged-out PTE as those scenarii should've been handled above.
1591 // These are all Windows checks
1592 //
1593 ASSERT(TempPte.u.Hard.Valid == 0);
1594 ASSERT(TempPte.u.Soft.Prototype == 0);
1595 ASSERT(TempPte.u.Soft.Transition == 0);
1596 ASSERT(TempPte.u.Soft.PageFileHigh == 0);
1597 ASSERT(TempPte.u.Long != 0);
1598
1599 //
1600 // If we got this far, the PTE can only be a demand zero PTE, which is what
1601 // we want. Go handle it!
1602 //
1603 Status = MiResolveDemandZeroFault(Address,
1604 PointerPte,
1605 Process,
1606 MM_NOIRQL);
1607 ASSERT(KeAreAllApcsDisabled() == TRUE);
1608 if (NT_SUCCESS(Status))
1609 {
1610 //
1611 // Make sure we're returning in a sane state and pass the status down
1612 //
1613 ASSERT(OldIrql == KeGetCurrentIrql());
1614 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1615 return Status;
1616 }
1617
1618 //
1619 // Generate an access fault
1620 //
1621 return STATUS_ACCESS_VIOLATION;
1622 }
1623
1624 NTSTATUS
1625 NTAPI
1626 MmArmAccessFault(IN BOOLEAN StoreInstruction,
1627 IN PVOID Address,
1628 IN KPROCESSOR_MODE Mode,
1629 IN PVOID TrapInformation)
1630 {
1631 KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
1632 PMMPTE ProtoPte = NULL;
1633 PMMPTE PointerPte = MiAddressToPte(Address);
1634 PMMPDE PointerPde = MiAddressToPde(Address);
1635 #if (_MI_PAGING_LEVELS >= 3)
1636 PMMPDE PointerPpe = MiAddressToPpe(Address);
1637 #if (_MI_PAGING_LEVELS == 4)
1638 PMMPDE PointerPxe = MiAddressToPxe(Address);
1639 #endif
1640 #endif
1641 MMPTE TempPte;
1642 PETHREAD CurrentThread;
1643 PEPROCESS CurrentProcess;
1644 NTSTATUS Status;
1645 PMMSUPPORT WorkingSet;
1646 ULONG ProtectionCode;
1647 PMMVAD Vad = NULL;
1648 PFN_NUMBER PageFrameIndex;
1649 ULONG Color;
1650 BOOLEAN IsSessionAddress;
1651 PMMPFN Pfn1;
1652 DPRINT("ARM3 FAULT AT: %p\n", Address);
1653
1654 /* Check for page fault on high IRQL */
1655 if (OldIrql > APC_LEVEL)
1656 {
1657 #if (_MI_PAGING_LEVELS < 3)
1658 /* Could be a page table for paged pool, which we'll allow */
1659 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1660 MiCheckPdeForPagedPool(Address);
1661 #endif
1662 /* Check if any of the top-level pages are invalid */
1663 if (
1664 #if (_MI_PAGING_LEVELS == 4)
1665 (PointerPxe->u.Hard.Valid == 0) ||
1666 #endif
1667 #if (_MI_PAGING_LEVELS >= 3)
1668 (PointerPpe->u.Hard.Valid == 0) ||
1669 #endif
1670 (PointerPde->u.Hard.Valid == 0) ||
1671 (PointerPte->u.Hard.Valid == 0))
1672 {
1673 /* This fault is not valid, print out some debugging help */
1674 DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
1675 Address,
1676 OldIrql);
1677 if (TrapInformation)
1678 {
1679 PKTRAP_FRAME TrapFrame = TrapInformation;
1680 #ifdef _M_IX86
1681 DbgPrint("MM:***EIP %p, EFL %p\n", TrapFrame->Eip, TrapFrame->EFlags);
1682 DbgPrint("MM:***EAX %p, ECX %p EDX %p\n", TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
1683 DbgPrint("MM:***EBX %p, ESI %p EDI %p\n", TrapFrame->Ebx, TrapFrame->Esi, TrapFrame->Edi);
1684 #elif defined(_M_AMD64)
1685 DbgPrint("MM:***RIP %p, EFL %p\n", TrapFrame->Rip, TrapFrame->EFlags);
1686 DbgPrint("MM:***RAX %p, RCX %p RDX %p\n", TrapFrame->Rax, TrapFrame->Rcx, TrapFrame->Rdx);
1687 DbgPrint("MM:***RBX %p, RSI %p RDI %p\n", TrapFrame->Rbx, TrapFrame->Rsi, TrapFrame->Rdi);
1688 #elif defined(_M_ARM)
1689 DbgPrint("MM:***PC %p\n", TrapFrame->Pc);
1690 DbgPrint("MM:***R0 %p, R1 %p R2 %p, R3 %p\n", TrapFrame->R0, TrapFrame->R1, TrapFrame->R2, TrapFrame->R3);
1691 DbgPrint("MM:***R11 %p, R12 %p SP %p, LR %p\n", TrapFrame->R11, TrapFrame->R12, TrapFrame->Sp, TrapFrame->Lr);
1692 #endif
1693 }
1694
1695 /* Tell the trap handler to fail */
1696 return STATUS_IN_PAGE_ERROR | 0x10000000;
1697 }
1698
1699 /* Not yet implemented in ReactOS */
1700 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
1701 ASSERT(((StoreInstruction) && MI_IS_PAGE_COPY_ON_WRITE(PointerPte)) == FALSE);
1702
1703 /* Check if this was a write */
1704 if (StoreInstruction)
1705 {
1706 /* Was it to a read-only page? */
1707 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1708 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1709 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1710 {
1711 /* Crash with distinguished bugcheck code */
1712 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1713 (ULONG_PTR)Address,
1714 PointerPte->u.Long,
1715 (ULONG_PTR)TrapInformation,
1716 10);
1717 }
1718 }
1719
1720 /* Nothing is actually wrong */
1721 DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
1722 return STATUS_SUCCESS;
1723 }
1724
1725 /* Check for kernel fault address */
1726 if (Address >= MmSystemRangeStart)
1727 {
1728 /* Bail out, if the fault came from user mode */
1729 if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
1730
1731 #if (_MI_PAGING_LEVELS == 2)
1732 if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
1733 MiCheckPdeForPagedPool(Address);
1734 #endif
1735
1736 /* Check if the higher page table entries are invalid */
1737 if (
1738 #if (_MI_PAGING_LEVELS == 4)
1739 /* AMD64 system, check if PXE is invalid */
1740 (PointerPxe->u.Hard.Valid == 0) ||
1741 #endif
1742 #if (_MI_PAGING_LEVELS >= 3)
1743 /* PAE/AMD64 system, check if PPE is invalid */
1744 (PointerPpe->u.Hard.Valid == 0) ||
1745 #endif
1746 /* Always check if the PDE is valid */
1747 (PointerPde->u.Hard.Valid == 0))
1748 {
1749 /* PXE/PPE/PDE (still) not valid, kill the system */
1750 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1751 (ULONG_PTR)Address,
1752 StoreInstruction,
1753 (ULONG_PTR)TrapInformation,
1754 2);
1755 }
1756
1757 /* Not handling session faults yet */
1758 IsSessionAddress = MI_IS_SESSION_ADDRESS(Address);
1759
1760 /* The PDE is valid, so read the PTE */
1761 TempPte = *PointerPte;
1762 if (TempPte.u.Hard.Valid == 1)
1763 {
1764 /* Check if this was system space or session space */
1765 if (!IsSessionAddress)
1766 {
1767 /* Check if the PTE is still valid under PFN lock */
1768 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1769 TempPte = *PointerPte;
1770 if (TempPte.u.Hard.Valid)
1771 {
1772 /* Check if this was a write */
1773 if (StoreInstruction)
1774 {
1775 /* Was it to a read-only page? */
1776 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1777 if (!(PointerPte->u.Long & PTE_READWRITE) &&
1778 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE))
1779 {
1780 /* Crash with distinguished bugcheck code */
1781 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1782 (ULONG_PTR)Address,
1783 PointerPte->u.Long,
1784 (ULONG_PTR)TrapInformation,
1785 11);
1786 }
1787 }
1788 }
1789
1790 /* Release PFN lock and return all good */
1791 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1792 return STATUS_SUCCESS;
1793 }
1794 }
1795 #if (_MI_PAGING_LEVELS == 2)
1796 /* Check if this was a session PTE that needs to remap the session PDE */
1797 if (MI_IS_SESSION_PTE(Address))
1798 {
1799 /* Do the remapping */
1800 Status = MiCheckPdeForSessionSpace(Address);
1801 if (!NT_SUCCESS(Status))
1802 {
1803 /* It failed, this address is invalid */
1804 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1805 (ULONG_PTR)Address,
1806 StoreInstruction,
1807 (ULONG_PTR)TrapInformation,
1808 6);
1809 }
1810 }
1811 #else
1812
1813 _WARN("Session space stuff is not implemented yet!")
1814
1815 #endif
1816
1817 /* Check for a fault on the page table or hyperspace */
1818 if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
1819 {
1820 #if (_MI_PAGING_LEVELS < 3)
1821 /* Windows does this check but I don't understand why -- it's done above! */
1822 ASSERT(MiCheckPdeForPagedPool(Address) != STATUS_WAIT_1);
1823 #endif
1824 /* Handle this as a user mode fault */
1825 goto UserFault;
1826 }
1827
1828 /* Get the current thread */
1829 CurrentThread = PsGetCurrentThread();
1830
1831 /* What kind of address is this */
1832 if (!IsSessionAddress)
1833 {
1834 /* Use the system working set */
1835 WorkingSet = &MmSystemCacheWs;
1836 CurrentProcess = NULL;
1837
1838 /* Make sure we don't have a recursive working set lock */
1839 if ((CurrentThread->OwnsProcessWorkingSetExclusive) ||
1840 (CurrentThread->OwnsProcessWorkingSetShared) ||
1841 (CurrentThread->OwnsSystemWorkingSetExclusive) ||
1842 (CurrentThread->OwnsSystemWorkingSetShared) ||
1843 (CurrentThread->OwnsSessionWorkingSetExclusive) ||
1844 (CurrentThread->OwnsSessionWorkingSetShared))
1845 {
1846 /* Fail */
1847 return STATUS_IN_PAGE_ERROR | 0x10000000;
1848 }
1849 }
1850 else
1851 {
1852 /* Use the session process and working set */
1853 CurrentProcess = HYDRA_PROCESS;
1854 WorkingSet = &MmSessionSpace->GlobalVirtualAddress->Vm;
1855
1856 /* Make sure we don't have a recursive working set lock */
1857 if ((CurrentThread->OwnsSessionWorkingSetExclusive) ||
1858 (CurrentThread->OwnsSessionWorkingSetShared))
1859 {
1860 /* Fail */
1861 return STATUS_IN_PAGE_ERROR | 0x10000000;
1862 }
1863 }
1864
1865 /* Acquire the working set lock */
1866 KeRaiseIrql(APC_LEVEL, &LockIrql);
1867 MiLockWorkingSet(CurrentThread, WorkingSet);
1868
1869 /* Re-read PTE now that we own the lock */
1870 TempPte = *PointerPte;
1871 if (TempPte.u.Hard.Valid == 1)
1872 {
1873 /* Check if this was a write */
1874 if (StoreInstruction)
1875 {
1876 /* Was it to a read-only page that is not copy on write? */
1877 Pfn1 = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
1878 if (!(TempPte.u.Long & PTE_READWRITE) &&
1879 !(Pfn1->OriginalPte.u.Soft.Protection & MM_READWRITE) &&
1880 !MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1881 {
1882 /* Case not yet handled */
1883 ASSERT(!IsSessionAddress);
1884
1885 /* Crash with distinguished bugcheck code */
1886 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1887 (ULONG_PTR)Address,
1888 TempPte.u.Long,
1889 (ULONG_PTR)TrapInformation,
1890 12);
1891 }
1892 }
1893
1894 /* Check for read-only write in session space */
1895 if ((IsSessionAddress) &&
1896 (StoreInstruction) &&
1897 !MI_IS_PAGE_WRITEABLE(&TempPte))
1898 {
1899 /* Sanity check */
1900 ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(Address));
1901
1902 /* Was this COW? */
1903 if (!MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
1904 {
1905 /* Then this is not allowed */
1906 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1907 (ULONG_PTR)Address,
1908 (ULONG_PTR)TempPte.u.Long,
1909 (ULONG_PTR)TrapInformation,
1910 13);
1911 }
1912
1913 /* Otherwise, handle COW */
1914 ASSERT(FALSE);
1915 }
1916
1917 /* Release the working set */
1918 MiUnlockWorkingSet(CurrentThread, WorkingSet);
1919 KeLowerIrql(LockIrql);
1920
1921 /* Otherwise, the PDE was probably invalid, and all is good now */
1922 return STATUS_SUCCESS;
1923 }
1924
1925 /* Check one kind of prototype PTE */
1926 if (TempPte.u.Soft.Prototype)
1927 {
1928 /* Make sure protected pool is on, and that this is a pool address */
1929 if ((MmProtectFreedNonPagedPool) &&
1930 (((Address >= MmNonPagedPoolStart) &&
1931 (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
1932 MmSizeOfNonPagedPoolInBytes))) ||
1933 ((Address >= MmNonPagedPoolExpansionStart) &&
1934 (Address < MmNonPagedPoolEnd))))
1935 {
1936 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
1937 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
1938 (ULONG_PTR)Address,
1939 StoreInstruction,
1940 Mode,
1941 4);
1942 }
1943
1944 /* Get the prototype PTE! */
1945 ProtoPte = MiProtoPteToPte(&TempPte);
1946
1947 /* Do we need to locate the prototype PTE in session space? */
1948 if ((IsSessionAddress) &&
1949 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))
1950 {
1951 /* Yep, go find it as well as the VAD for it */
1952 ProtoPte = MiCheckVirtualAddress(Address,
1953 &ProtectionCode,
1954 &Vad);
1955 ASSERT(ProtoPte != NULL);
1956 }
1957 }
1958 else
1959 {
1960 /* We don't implement transition PTEs */
1961 ASSERT(TempPte.u.Soft.Transition == 0);
1962
1963 /* Check for no-access PTE */
1964 if (TempPte.u.Soft.Protection == MM_NOACCESS)
1965 {
1966 /* Bugcheck the system! */
1967 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1968 (ULONG_PTR)Address,
1969 StoreInstruction,
1970 (ULONG_PTR)TrapInformation,
1971 1);
1972 }
1973
1974 /* Check for no protecton at all */
1975 if (TempPte.u.Soft.Protection == MM_ZERO_ACCESS)
1976 {
1977 /* Bugcheck the system! */
1978 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
1979 (ULONG_PTR)Address,
1980 StoreInstruction,
1981 (ULONG_PTR)TrapInformation,
1982 0);
1983 }
1984 }
1985
1986 /* Check for demand page */
1987 if ((StoreInstruction) &&
1988 !(ProtoPte) &&
1989 !(IsSessionAddress) &&
1990 !(TempPte.u.Hard.Valid))
1991 {
1992 /* Get the protection code */
1993 ASSERT(TempPte.u.Soft.Transition == 0);
1994 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
1995 {
1996 /* Bugcheck the system! */
1997 KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
1998 (ULONG_PTR)Address,
1999 TempPte.u.Long,
2000 (ULONG_PTR)TrapInformation,
2001 14);
2002 }
2003 }
2004
2005 /* Now do the real fault handling */
2006 Status = MiDispatchFault(StoreInstruction,
2007 Address,
2008 PointerPte,
2009 ProtoPte,
2010 FALSE,
2011 CurrentProcess,
2012 TrapInformation,
2013 NULL);
2014
2015 /* Release the working set */
2016 ASSERT(KeAreAllApcsDisabled() == TRUE);
2017 MiUnlockWorkingSet(CurrentThread, WorkingSet);
2018 KeLowerIrql(LockIrql);
2019
2020 /* We are done! */
2021 DPRINT("Fault resolved with status: %lx\n", Status);
2022 return Status;
2023 }
2024
2025 /* This is a user fault */
2026 UserFault:
2027 CurrentThread = PsGetCurrentThread();
2028 CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
2029
2030 /* Lock the working set */
2031 MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
2032
2033 #if (_MI_PAGING_LEVELS == 4)
2034 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2035 // also this is missing the page count increment
2036 /* Check if the PXE is valid */
2037 if (PointerPxe->u.Hard.Valid == 0)
2038 {
2039 /* Right now, we only handle scenarios where the PXE is totally empty */
2040 ASSERT(PointerPxe->u.Long == 0);
2041 #if 0
2042 /* Resolve a demand zero fault */
2043 Status = MiResolveDemandZeroFault(PointerPpe,
2044 MM_READWRITE,
2045 CurrentProcess,
2046 MM_NOIRQL);
2047 #endif
2048 /* We should come back with a valid PXE */
2049 ASSERT(PointerPxe->u.Hard.Valid == 1);
2050 }
2051 #endif
2052
2053 #if (_MI_PAGING_LEVELS >= 3)
2054 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
2055 // also this is missing the page count increment
2056 /* Check if the PPE is valid */
2057 if (PointerPpe->u.Hard.Valid == 0)
2058 {
2059 /* Right now, we only handle scenarios where the PPE is totally empty */
2060 ASSERT(PointerPpe->u.Long == 0);
2061 #if 0
2062 /* Resolve a demand zero fault */
2063 Status = MiResolveDemandZeroFault(PointerPde,
2064 MM_READWRITE,
2065 CurrentProcess,
2066 MM_NOIRQL);
2067 #endif
2068 /* We should come back with a valid PPE */
2069 ASSERT(PointerPpe->u.Hard.Valid == 1);
2070 }
2071 #endif
2072
2073 /* Check if the PDE is valid */
2074 if (PointerPde->u.Hard.Valid == 0)
2075 {
2076 /* Right now, we only handle scenarios where the PDE is totally empty */
2077 ASSERT(PointerPde->u.Long == 0);
2078
2079 /* And go dispatch the fault on the PDE. This should handle the demand-zero */
2080 #if MI_TRACE_PFNS
2081 UserPdeFault = TRUE;
2082 #endif
2083 MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2084 if (ProtectionCode == MM_NOACCESS)
2085 {
2086 #if (_MI_PAGING_LEVELS == 2)
2087 /* Could be a page table for paged pool */
2088 MiCheckPdeForPagedPool(Address);
2089 #endif
2090 /* Has the code above changed anything -- is this now a valid PTE? */
2091 Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2092
2093 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2094 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2095 return Status;
2096 }
2097
2098 /* Write a demand-zero PDE */
2099 MI_WRITE_INVALID_PDE(PointerPde, DemandZeroPde);
2100
2101 /* Dispatch the fault */
2102 Status = MiDispatchFault(TRUE,
2103 PointerPte,
2104 PointerPde,
2105 NULL,
2106 FALSE,
2107 PsGetCurrentProcess(),
2108 TrapInformation,
2109 NULL);
2110 #if MI_TRACE_PFNS
2111 UserPdeFault = FALSE;
2112 #endif
2113 /* We should come back with APCs enabled, and with a valid PDE */
2114 ASSERT(KeAreAllApcsDisabled() == TRUE);
2115 ASSERT(PointerPde->u.Hard.Valid == 1);
2116 }
2117 else
2118 {
2119 /* Not yet implemented in ReactOS */
2120 ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
2121 }
2122
2123 /* Now capture the PTE. */
2124 TempPte = *PointerPte;
2125
2126 /* Check if the PTE is valid */
2127 if (TempPte.u.Hard.Valid)
2128 {
2129 /* Check if this is a write on a readonly PTE */
2130 if (StoreInstruction)
2131 {
2132 /* Is this a copy on write PTE? */
2133 if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
2134 {
2135 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
2136 PMMPFN Pfn1;
2137
2138 LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2139
2140 ASSERT(MmAvailablePages > 0);
2141
2142 /* Allocate a new page and copy it */
2143 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
2144 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
2145
2146 MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
2147
2148 /* Dereference whatever this PTE is referencing */
2149 Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
2150 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
2151 ASSERT(!MI_IS_PFN_DELETED(Pfn1));
2152 ProtoPte = Pfn1->PteAddress;
2153 MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
2154
2155 /* And make a new shiny one with our page */
2156 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
2157 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
2158 TempPte.u.Hard.Write = 1;
2159 TempPte.u.Hard.CopyOnWrite = 0;
2160
2161 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2162
2163 KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
2164
2165 /* Return the status */
2166 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2167 return STATUS_PAGE_FAULT_COPY_ON_WRITE;
2168 }
2169
2170 /* Is this a read-only PTE? */
2171 if (!MI_IS_PAGE_WRITEABLE(&TempPte))
2172 {
2173 /* Return the status */
2174 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2175 return STATUS_ACCESS_VIOLATION;
2176 }
2177 }
2178
2179 /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
2180
2181 /* The fault has already been resolved by a different thread */
2182 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2183 return STATUS_SUCCESS;
2184 }
2185
2186 /* Quick check for demand-zero */
2187 if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
2188 {
2189 /* Resolve the fault */
2190 MiResolveDemandZeroFault(Address,
2191 PointerPte,
2192 CurrentProcess,
2193 MM_NOIRQL);
2194
2195 /* Return the status */
2196 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2197 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2198 }
2199
2200 /* Check for zero PTE */
2201 if (TempPte.u.Long == 0)
2202 {
2203 /* Check if this address range belongs to a valid allocation (VAD) */
2204 ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
2205 if (ProtectionCode == MM_NOACCESS)
2206 {
2207 #if (_MI_PAGING_LEVELS == 2)
2208 /* Could be a page table for paged pool */
2209 MiCheckPdeForPagedPool(Address);
2210 #endif
2211 /* Has the code above changed anything -- is this now a valid PTE? */
2212 Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
2213
2214 /* Either this was a bogus VA or we've fixed up a paged pool PDE */
2215 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2216 return Status;
2217 }
2218
2219 /*
2220 * Check if this is a real user-mode address or actually a kernel-mode
2221 * page table for a user mode address
2222 */
2223 if (Address <= MM_HIGHEST_USER_ADDRESS)
2224 {
2225 /* Add an additional page table reference */
2226 MiIncrementPageTableReferences(Address);
2227 }
2228
2229 /* Is this a guard page? */
2230 if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
2231 {
2232 /* The VAD protection cannot be MM_DECOMMIT! */
2233 ASSERT(ProtectionCode != MM_DECOMMIT);
2234
2235 /* Remove the bit */
2236 TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
2237 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2238
2239 /* Not supported */
2240 ASSERT(ProtoPte == NULL);
2241 ASSERT(CurrentThread->ApcNeeded == 0);
2242
2243 /* Drop the working set lock */
2244 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2245 ASSERT(KeGetCurrentIrql() == OldIrql);
2246
2247 /* Handle stack expansion */
2248 return MiCheckForUserStackOverflow(Address, TrapInformation);
2249 }
2250
2251 /* Did we get a prototype PTE back? */
2252 if (!ProtoPte)
2253 {
2254 /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
2255 if (PointerPde == MiAddressToPde(PTE_BASE))
2256 {
2257 /* Then it's really a demand-zero PDE (on behalf of user-mode) */
2258 #ifdef _M_ARM
2259 _WARN("This is probably completely broken!");
2260 MI_WRITE_INVALID_PDE((PMMPDE)PointerPte, DemandZeroPde);
2261 #else
2262 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
2263 #endif
2264 }
2265 else
2266 {
2267 /* No, create a new PTE. First, write the protection */
2268 TempPte.u.Soft.Protection = ProtectionCode;
2269 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2270 }
2271
2272 /* Lock the PFN database since we're going to grab a page */
2273 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2274
2275 /* Make sure we have enough pages */
2276 ASSERT(MmAvailablePages >= 32);
2277
2278 /* Try to get a zero page */
2279 MI_SET_USAGE(MI_USAGE_PEB_TEB);
2280 MI_SET_PROCESS2(CurrentProcess->ImageFileName);
2281 Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
2282 PageFrameIndex = MiRemoveZeroPageSafe(Color);
2283 if (!PageFrameIndex)
2284 {
2285 /* Grab a page out of there. Later we should grab a colored zero page */
2286 PageFrameIndex = MiRemoveAnyPage(Color);
2287 ASSERT(PageFrameIndex);
2288
2289 /* Release the lock since we need to do some zeroing */
2290 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2291
2292 /* Zero out the page, since it's for user-mode */
2293 MiZeroPfn(PageFrameIndex);
2294
2295 /* Grab the lock again so we can initialize the PFN entry */
2296 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2297 }
2298
2299 /* Initialize the PFN entry now */
2300 MiInitializePfn(PageFrameIndex, PointerPte, 1);
2301
2302 /* And we're done with the lock */
2303 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2304
2305 /* Increment the count of pages in the process */
2306 CurrentProcess->NumberOfPrivatePages++;
2307
2308 /* One more demand-zero fault */
2309 InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount);
2310
2311 /* Fault on user PDE, or fault on user PTE? */
2312 if (PointerPte <= MiHighestUserPte)
2313 {
2314 /* User fault, build a user PTE */
2315 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2316 PointerPte,
2317 PointerPte->u.Soft.Protection,
2318 PageFrameIndex);
2319 }
2320 else
2321 {
2322 /* This is a user-mode PDE, create a kernel PTE for it */
2323 MI_MAKE_HARDWARE_PTE(&TempPte,
2324 PointerPte,
2325 PointerPte->u.Soft.Protection,
2326 PageFrameIndex);
2327 }
2328
2329 /* Write the dirty bit for writeable pages */
2330 if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
2331
2332 /* And now write down the PTE, making the address valid */
2333 MI_WRITE_VALID_PTE(PointerPte, TempPte);
2334 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
2335 ASSERT(Pfn1->u1.Event == NULL);
2336
2337 /* Demand zero */
2338 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2339 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2340 return STATUS_PAGE_FAULT_DEMAND_ZERO;
2341 }
2342
2343 /* We should have a valid protection here */
2344 ASSERT(ProtectionCode != 0x100);
2345
2346 /* Write the prototype PTE */
2347 TempPte = PrototypePte;
2348 TempPte.u.Soft.Protection = ProtectionCode;
2349 ASSERT(TempPte.u.Long != 0);
2350 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2351 }
2352 else
2353 {
2354 /* Get the protection code and check if this is a proto PTE */
2355 ProtectionCode = (ULONG)TempPte.u.Soft.Protection;
2356 if (TempPte.u.Soft.Prototype)
2357 {
2358 /* Do we need to go find the real PTE? */
2359 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
2360 {
2361 /* Get the prototype pte and VAD for it */
2362 ProtoPte = MiCheckVirtualAddress(Address,
2363 &ProtectionCode,
2364 &Vad);
2365 if (!ProtoPte)
2366 {
2367 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2368 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2369 return STATUS_ACCESS_VIOLATION;
2370 }
2371 }
2372 else
2373 {
2374 /* Get the prototype PTE! */
2375 ProtoPte = MiProtoPteToPte(&TempPte);
2376
2377 /* Is it read-only */
2378 if (TempPte.u.Proto.ReadOnly)
2379 {
2380 /* Set read-only code */
2381 ProtectionCode = MM_READONLY;
2382 }
2383 else
2384 {
2385 /* Set unknown protection */
2386 ProtectionCode = 0x100;
2387 ASSERT(CurrentProcess->CloneRoot != NULL);
2388 }
2389 }
2390 }
2391 }
2392
2393 /* Do we have a valid protection code? */
2394 if (ProtectionCode != 0x100)
2395 {
2396 /* Run a software access check first, including to detect guard pages */
2397 Status = MiAccessCheck(PointerPte,
2398 StoreInstruction,
2399 Mode,
2400 ProtectionCode,
2401 TrapInformation,
2402 FALSE);
2403 if (Status != STATUS_SUCCESS)
2404 {
2405 /* Not supported */
2406 ASSERT(CurrentThread->ApcNeeded == 0);
2407
2408 /* Drop the working set lock */
2409 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2410 ASSERT(KeGetCurrentIrql() == OldIrql);
2411
2412 /* Did we hit a guard page? */
2413 if (Status == STATUS_GUARD_PAGE_VIOLATION)
2414 {
2415 /* Handle stack expansion */
2416 return MiCheckForUserStackOverflow(Address, TrapInformation);
2417 }
2418
2419 /* Otherwise, fail back to the caller directly */
2420 return Status;
2421 }
2422 }
2423
2424 /* Dispatch the fault */
2425 Status = MiDispatchFault(StoreInstruction,
2426 Address,
2427 PointerPte,
2428 ProtoPte,
2429 FALSE,
2430 CurrentProcess,
2431 TrapInformation,
2432 Vad);
2433
2434 /* Return the status */
2435 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
2436 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
2437 return Status;
2438 }
2439
2440 NTSTATUS
2441 NTAPI
2442 MmGetExecuteOptions(IN PULONG ExecuteOptions)
2443 {
2444 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2445 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2446
2447 *ExecuteOptions = 0;
2448
2449 if (CurrentProcess->Flags.ExecuteDisable)
2450 {
2451 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
2452 }
2453
2454 if (CurrentProcess->Flags.ExecuteEnable)
2455 {
2456 *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
2457 }
2458
2459 if (CurrentProcess->Flags.DisableThunkEmulation)
2460 {
2461 *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
2462 }
2463
2464 if (CurrentProcess->Flags.Permanent)
2465 {
2466 *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
2467 }
2468
2469 if (CurrentProcess->Flags.ExecuteDispatchEnable)
2470 {
2471 *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
2472 }
2473
2474 if (CurrentProcess->Flags.ImageDispatchEnable)
2475 {
2476 *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
2477 }
2478
2479 return STATUS_SUCCESS;
2480 }
2481
2482 NTSTATUS
2483 NTAPI
2484 MmSetExecuteOptions(IN ULONG ExecuteOptions)
2485 {
2486 PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
2487 KLOCK_QUEUE_HANDLE ProcessLock;
2488 NTSTATUS Status = STATUS_ACCESS_DENIED;
2489 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2490
2491 /* Only accept valid flags */
2492 if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
2493 {
2494 /* Fail */
2495 DPRINT1("Invalid no-execute options\n");
2496 return STATUS_INVALID_PARAMETER;
2497 }
2498
2499 /* Change the NX state in the process lock */
2500 KiAcquireProcessLock(CurrentProcess, &ProcessLock);
2501
2502 /* Don't change anything if the permanent flag was set */
2503 if (!CurrentProcess->Flags.Permanent)
2504 {
2505 /* Start by assuming it's not disabled */
2506 CurrentProcess->Flags.ExecuteDisable = FALSE;
2507
2508 /* Now process each flag and turn the equivalent bit on */
2509 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
2510 {
2511 CurrentProcess->Flags.ExecuteDisable = TRUE;
2512 }
2513 if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
2514 {
2515 CurrentProcess->Flags.ExecuteEnable = TRUE;
2516 }
2517 if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
2518 {
2519 CurrentProcess->Flags.DisableThunkEmulation = TRUE;
2520 }
2521 if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
2522 {
2523 CurrentProcess->Flags.Permanent = TRUE;
2524 }
2525 if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
2526 {
2527 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2528 }
2529 if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
2530 {
2531 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2532 }
2533
2534 /* These are turned on by default if no-execution is also eanbled */
2535 if (CurrentProcess->Flags.ExecuteEnable)
2536 {
2537 CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
2538 CurrentProcess->Flags.ImageDispatchEnable = TRUE;
2539 }
2540
2541 /* All good */
2542 Status = STATUS_SUCCESS;
2543 }
2544
2545 /* Release the lock and return status */
2546 KiReleaseProcessLock(&ProcessLock);
2547 return Status;
2548 }
2549
2550 /* EOF */