2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
10 /* So long, and Thanks for All the Fish */
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
32 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
34 IN ULONG ProtectionMask
,
36 IN BOOLEAN CaptureDirtyBit
);
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
44 IN ULONG_PTR EndingAddress
,
48 PMMPTE PointerPte
, LastPte
, PointerPde
;
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde
= MiAddressToPde(StartingAddress
);
53 PointerPte
= MiAddressToPte(StartingAddress
);
54 LastPte
= MiAddressToPte(EndingAddress
);
56 /* Handle commited pages first */
57 if (Vad
->u
.VadFlags
.MemCommit
== 1)
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
62 /* Is the PDE demand-zero? */
63 PointerPde
= MiAddressToPte(PointerPte
);
64 if (PointerPde
->u
.Long
!= 0)
66 /* It is not. Is it valid? */
67 if (PointerPde
->u
.Hard
.Valid
== 0)
70 PointerPte
= MiPteToAddress(PointerPde
);
71 MiMakeSystemAddressValid(PointerPte
, Process
);
76 /* It is, skip it and move to the next PDE, unless we're done */
78 PointerPte
= MiPteToAddress(PointerPde
);
79 if (PointerPte
> LastPte
) return CommittedPages
;
82 /* Now loop all the PTEs in the range */
83 while (PointerPte
<= LastPte
)
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte
))
88 /* Is this PDE demand zero? */
89 PointerPde
= MiAddressToPte(PointerPte
);
90 if (PointerPde
->u
.Long
!= 0)
92 /* It isn't -- is it valid? */
93 if (PointerPde
->u
.Hard
.Valid
== 0)
95 /* Nope, fault it in */
96 PointerPte
= MiPteToAddress(PointerPde
);
97 MiMakeSystemAddressValid(PointerPte
, Process
);
102 /* It is, skip it and move to the next PDE */
104 PointerPte
= MiPteToAddress(PointerPde
);
109 /* Is this PTE demand zero? */
110 if (PointerPte
->u
.Long
!= 0)
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
114 (PointerPte
->u
.Hard
.Valid
== 0) &&
115 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
116 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
118 /* It is, so remove it from the count of commited pages */
123 /* Move to the next PTE */
127 /* Return how many committed pages there still are */
128 return CommittedPages
;
131 /* This is a non-commited VAD, so assume none of it is committed */
134 /* Is the PDE demand-zero? */
135 PointerPde
= MiAddressToPte(PointerPte
);
136 if (PointerPde
->u
.Long
!= 0)
138 /* It isn't -- is it invalid? */
139 if (PointerPde
->u
.Hard
.Valid
== 0)
141 /* It is, so page it in */
142 PointerPte
= MiPteToAddress(PointerPde
);
143 MiMakeSystemAddressValid(PointerPte
, Process
);
148 /* It is, so skip it and move to the next PDE */
150 PointerPte
= MiPteToAddress(PointerPde
);
151 if (PointerPte
> LastPte
) return CommittedPages
;
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte
<= LastPte
)
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte
))
160 /* Is this new PDE demand-zero? */
161 PointerPde
= MiAddressToPte(PointerPte
);
162 if (PointerPde
->u
.Long
!= 0)
164 /* It isn't. Is it valid? */
165 if (PointerPde
->u
.Hard
.Valid
== 0)
167 /* It isn't, so make it valid */
168 PointerPte
= MiPteToAddress(PointerPde
);
169 MiMakeSystemAddressValid(PointerPte
, Process
);
174 /* It is, so skip it and move to the next one */
176 PointerPte
= MiPteToAddress(PointerPde
);
181 /* Is this PTE demand-zero? */
182 if (PointerPte
->u
.Long
!= 0)
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
186 (PointerPte
->u
.Hard
.Valid
== 1) ||
187 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
188 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
190 /* It is! So we'll treat this as a committed page */
195 /* Move to the next PTE */
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages
;
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
206 IN PEPROCESS CurrentProcess
)
209 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
210 PETHREAD CurrentThread
= PsGetCurrentThread();
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
214 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
215 (PageTableVirtualAddress
> MmPagedPoolEnd
));
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE
);
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress
))
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
230 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
231 if (!NT_SUCCESS(Status
))
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
237 (ULONG_PTR
)CurrentProcess
,
238 (ULONG_PTR
)PageTableVirtualAddress
);
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess
,
247 /* This flag will be useful later when we do better locking */
251 /* Let caller know what the lock state is */
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
261 BOOLEAN LockChange
= FALSE
;
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress
))
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
273 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
274 if (!NT_SUCCESS(Status
))
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
281 (ULONG_PTR
)VirtualAddress
);
284 /* This flag will be useful later when we do better locking */
287 /* Lock the PFN database */
288 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
291 /* Let caller know what the lock state is */
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
298 IN PFN_NUMBER PageCount
,
300 OUT PPFN_NUMBER ValidPages
)
302 PFN_COUNT ActualPages
= 0;
303 PETHREAD CurrentThread
= PsGetCurrentThread();
305 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
315 /* Make sure there's some data about the page */
316 if (PointerPte
->u
.Long
)
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
320 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte
->u
.Hard
.Valid
)
325 /* Get the page PFN */
326 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
327 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1
->u1
.WsIndex
== 0);
332 /* Actual valid, legitimate, pages */
333 if (ValidPages
) (*ValidPages
)++;
335 /* Get the page table entry */
336 PageTableIndex
= Pfn1
->u4
.PteFrame
;
337 Pfn2
= MiGetPfnEntry(PageTableIndex
);
339 /* Lock the PFN database */
340 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1
);
344 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2
, PageTableIndex
);
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte
);
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
362 * Right now, we shouldn't expect any page file information in the PTE
364 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte
);
370 /* Actual legitimate pages */
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE
, TRUE
);
391 MiDeletePte(IN PMMPTE PointerPte
,
392 IN PVOID VirtualAddress
,
393 IN PEPROCESS CurrentProcess
,
394 IN PMMPTE PrototypePte
)
398 PFN_NUMBER PageFrameIndex
;
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
404 /* Capture the PTE */
405 TempPte
= *PointerPte
;
407 /* We only support valid PTEs for now */
408 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
409 if (TempPte
.u
.Hard
.Valid
== 0)
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
413 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde
= MiPteToPde(PointerPte
);
425 if (PointerPde
->u
.Hard
.Valid
== 0)
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT
,
435 (ULONG_PTR
)PointerPte
,
437 (ULONG_PTR
)VirtualAddress
);
439 #if (_MI_PAGING_LEVELS == 2)
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
450 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT
,
455 (ULONG_PTR
)PointerPte
,
456 (ULONG_PTR
)PrototypePte
,
457 (ULONG_PTR
)Pfn1
->PteAddress
);
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT
,
469 (ULONG_PTR
)PointerPte
,
471 (ULONG_PTR
)Pfn1
->PteAddress
);
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1
->u2
.ShareCount
== 1);
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1
);
482 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
488 /* Destroy the PTE and flush the TLB */
489 MI_ERASE_PTE(PointerPte
);
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
496 IN ULONG_PTR EndingAddress
,
499 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
502 PEPROCESS CurrentProcess
;
504 BOOLEAN AddressGap
= FALSE
;
505 PSUBSECTION Subsection
;
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess
= PsGetCurrentProcess();
512 PointerPde
= MiAddressToPde(Va
);
513 PointerPte
= MiAddressToPte(Va
);
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
518 /* Don't worry about prototypes */
519 PrototypePte
= LastPrototypePte
= NULL
;
523 /* Get the prototype PTE */
524 PrototypePte
= Vad
->FirstPrototypePte
;
525 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
531 /* Loop the PTE for each VA */
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde
->u
.Long
)
537 /* There are gaps in the address space */
540 /* Still no valid PDE, try the next 4MB (or whatever) */
543 /* Update the PTE on this new boundary */
544 PointerPte
= MiPteToAddress(PointerPde
);
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
548 if (Va
> EndingAddress
) return;
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde
->u
.Hard
.Valid
)
554 /* It isn't, so map it in */
555 PointerPte
= MiPteToAddress(PointerPde
);
556 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
561 ASSERT(Va
<= EndingAddress
);
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap
) && (LastPrototypePte
))
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
574 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
578 /* No more subsections, we are done with prototype PTEs */
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
587 /* Capture the PDE and make sure it exists */
588 TempPte
= *PointerPte
;
591 MiDecrementPageTableReferences((PVOID
)Va
);
593 /* Check if the PTE is actually mapped in */
594 if (MI_IS_MAPPED_PTE(&TempPte
))
596 /* Are we dealing with section VAD? */
597 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
599 /* We need to skip to the next correct prototype PTE */
600 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
602 /* And we need the subsection to skip to the next last prototype PTE */
603 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
607 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
611 /* No more subsections, we are done with prototype PTEs */
616 /* Check for prototype PTE */
617 if ((TempPte
.u
.Hard
.Valid
== 0) &&
618 (TempPte
.u
.Soft
.Prototype
== 1))
621 MI_ERASE_PTE(PointerPte
);
625 /* Delete the PTE proper */
626 MiDeletePte(PointerPte
,
634 /* The PTE was never mapped, just nuke it here */
635 MI_ERASE_PTE(PointerPte
);
639 /* Update the address and PTE for it */
644 /* Making sure the PDE is still valid */
645 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
647 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
649 /* The PDE should still be valid at this point */
650 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
652 /* Check remaining PTE count (go back 1 page due to above loop) */
653 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
655 if (PointerPde
->u
.Long
!= 0)
657 /* Delete the PTE proper */
658 MiDeletePte(PointerPde
,
659 MiPteToAddress(PointerPde
),
665 /* Release the lock and get out if we're done */
666 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
667 if (Va
> EndingAddress
) return;
669 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
670 PointerPde
= MiAddressToPde(Va
);
676 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
677 OUT PBOOLEAN HaveBadAddress
,
678 OUT PULONG_PTR BadAddress
)
680 PEXCEPTION_RECORD ExceptionRecord
;
686 *HaveBadAddress
= FALSE
;
689 // Get the exception record
691 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
694 // Look at the exception code
696 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
697 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
698 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
701 // We can tell the address if we have more than one parameter
703 if (ExceptionRecord
->NumberParameters
> 1)
706 // Return the address
708 *HaveBadAddress
= TRUE
;
709 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
714 // Continue executing the next handler
716 return EXCEPTION_EXECUTE_HANDLER
;
721 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
722 IN PVOID SourceAddress
,
723 IN PEPROCESS TargetProcess
,
724 OUT PVOID TargetAddress
,
725 IN SIZE_T BufferSize
,
726 IN KPROCESSOR_MODE PreviousMode
,
727 OUT PSIZE_T ReturnSize
)
729 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
730 PMDL Mdl
= (PMDL
)MdlBuffer
;
731 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
732 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
733 volatile BOOLEAN PagesLocked
;
734 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
735 volatile PVOID MdlAddress
;
737 BOOLEAN HaveBadAddress
;
738 ULONG_PTR BadAddress
;
739 NTSTATUS Status
= STATUS_SUCCESS
;
743 // Calculate the maximum amount of data to move
745 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
746 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
747 CurrentSize
= TotalSize
;
748 RemainingSize
= BufferSize
;
751 // Loop as long as there is still data
753 while (RemainingSize
> 0)
756 // Check if this transfer will finish everything off
758 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
761 // Attach to the source address space
763 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
766 // Reset state for this pass
770 FailedInMoving
= FALSE
;
771 ASSERT(FailedInProbe
== FALSE
);
774 // Protect user-mode copy
779 // If this is our first time, probe the buffer
781 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
784 // Catch a failure here
786 FailedInProbe
= TRUE
;
791 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
796 FailedInProbe
= FALSE
;
800 // Initialize and probe and lock the MDL
802 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
803 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
809 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
818 // Use our SEH handler to pick this up
820 FailedInMapping
= TRUE
;
821 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
825 // Now let go of the source and grab to the target process
827 KeUnstackDetachProcess(&ApcState
);
828 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
831 // Check if this is our first time through
833 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
836 // Catch a failure here
838 FailedInProbe
= TRUE
;
843 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
848 FailedInProbe
= FALSE
;
852 // Now do the actual move
854 FailedInMoving
= TRUE
;
855 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
857 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
862 // Detach from whoever we may be attached to
864 KeUnstackDetachProcess(&ApcState
);
867 // Check if we had mapped the pages
869 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
872 // Check if we had locked the pages
874 if (PagesLocked
) MmUnlockPages(Mdl
);
877 // Check if we hit working set quota
879 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
884 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
888 // Check if we failed during the probe or mapping
890 if ((FailedInProbe
) || (FailedInMapping
))
895 Status
= _SEH2_GetExceptionCode();
896 _SEH2_YIELD(return Status
);
900 // Otherwise, we failed probably during the move
902 *ReturnSize
= BufferSize
- RemainingSize
;
906 // Check if we know exactly where we stopped copying
911 // Return the exact number of bytes copied
913 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
918 // Return partial copy
920 Status
= STATUS_PARTIAL_COPY
;
925 // Check for SEH status
927 if (Status
!= STATUS_SUCCESS
) return Status
;
930 // Detach from target
932 KeUnstackDetachProcess(&ApcState
);
937 MmUnmapLockedPages(MdlAddress
, Mdl
);
941 // Update location and size
943 RemainingSize
-= CurrentSize
;
944 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
945 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
951 *ReturnSize
= BufferSize
;
952 return STATUS_SUCCESS
;
957 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
958 IN PVOID SourceAddress
,
959 IN PEPROCESS TargetProcess
,
960 OUT PVOID TargetAddress
,
961 IN SIZE_T BufferSize
,
962 IN KPROCESSOR_MODE PreviousMode
,
963 OUT PSIZE_T ReturnSize
)
965 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
966 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
967 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
968 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
971 BOOLEAN HaveBadAddress
;
972 ULONG_PTR BadAddress
;
973 NTSTATUS Status
= STATUS_SUCCESS
;
977 // Calculate the maximum amount of data to move
979 TotalSize
= MI_MAX_TRANSFER_SIZE
;
980 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
981 CurrentSize
= TotalSize
;
982 RemainingSize
= BufferSize
;
985 // Check if we can use the stack
987 if (BufferSize
<= MI_POOL_COPY_BYTES
)
992 PoolAddress
= (PVOID
)StackBuffer
;
999 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1000 if (!PoolAddress
) ASSERT(FALSE
);
1001 HavePoolAddress
= TRUE
;
1005 // Loop as long as there is still data
1007 while (RemainingSize
> 0)
1010 // Check if this transfer will finish everything off
1012 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1015 // Attach to the source address space
1017 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1020 // Reset state for this pass
1022 FailedInMoving
= FALSE
;
1023 ASSERT(FailedInProbe
== FALSE
);
1026 // Protect user-mode copy
1031 // If this is our first time, probe the buffer
1033 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1036 // Catch a failure here
1038 FailedInProbe
= TRUE
;
1043 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1048 FailedInProbe
= FALSE
;
1054 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1057 // Now let go of the source and grab to the target process
1059 KeUnstackDetachProcess(&ApcState
);
1060 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1063 // Check if this is our first time through
1065 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1068 // Catch a failure here
1070 FailedInProbe
= TRUE
;
1075 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1080 FailedInProbe
= FALSE
;
1084 // Now do the actual move
1086 FailedInMoving
= TRUE
;
1087 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1089 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1094 // Detach from whoever we may be attached to
1096 KeUnstackDetachProcess(&ApcState
);
1099 // Check if we had allocated pool
1101 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1104 // Check if we failed during the probe
1111 Status
= _SEH2_GetExceptionCode();
1112 _SEH2_YIELD(return Status
);
1116 // Otherwise, we failed, probably during the move
1118 *ReturnSize
= BufferSize
- RemainingSize
;
1122 // Check if we know exactly where we stopped copying
1127 // Return the exact number of bytes copied
1129 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1134 // Return partial copy
1136 Status
= STATUS_PARTIAL_COPY
;
1141 // Check for SEH status
1143 if (Status
!= STATUS_SUCCESS
) return Status
;
1146 // Detach from target
1148 KeUnstackDetachProcess(&ApcState
);
1151 // Update location and size
1153 RemainingSize
-= CurrentSize
;
1154 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1155 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1160 // Check if we had allocated pool
1162 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1167 *ReturnSize
= BufferSize
;
1168 return STATUS_SUCCESS
;
1173 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1174 IN PVOID SourceAddress
,
1175 IN PEPROCESS TargetProcess
,
1176 OUT PVOID TargetAddress
,
1177 IN SIZE_T BufferSize
,
1178 IN KPROCESSOR_MODE PreviousMode
,
1179 OUT PSIZE_T ReturnSize
)
1182 PEPROCESS Process
= SourceProcess
;
1185 // Don't accept zero-sized buffers
1187 if (!BufferSize
) return STATUS_SUCCESS
;
1190 // If we are copying from ourselves, lock the target instead
1192 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1195 // Acquire rundown protection
1197 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1202 return STATUS_PROCESS_IS_TERMINATING
;
1206 // See if we should use the pool copy
1208 if (BufferSize
> MI_POOL_COPY_BYTES
)
1213 Status
= MiDoMappedCopy(SourceProcess
,
1226 Status
= MiDoPoolCopy(SourceProcess
,
1238 ExReleaseRundownProtection(&Process
->RundownProtect
);
1244 MmFlushVirtualMemory(IN PEPROCESS Process
,
1245 IN OUT PVOID
*BaseAddress
,
1246 IN OUT PSIZE_T RegionSize
,
1247 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1255 return STATUS_SUCCESS
;
1260 MiGetPageProtection(IN PMMPTE PointerPte
)
1266 /* Copy this PTE's contents */
1267 TempPte
= *PointerPte
;
1269 /* Assure it's not totally zero */
1270 ASSERT(TempPte
.u
.Long
);
1272 /* Check for a special prototype format */
1273 if (TempPte
.u
.Soft
.Valid
== 0 &&
1274 TempPte
.u
.Soft
.Prototype
== 1)
1276 /* Unsupported now */
1281 /* In the easy case of transition or demand zero PTE just return its protection */
1282 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1284 /* If we get here, the PTE is valid, so look up the page in PFN database */
1285 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1286 if (!Pfn
->u3
.e1
.PrototypePte
)
1288 /* Return protection of the original pte */
1289 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1290 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1293 /* This is software PTE */
1294 DPRINT1("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1295 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte
));
1296 DPRINT1("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1297 DPRINT1("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1298 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1303 MiQueryAddressState(IN PVOID Va
,
1305 IN PEPROCESS TargetProcess
,
1306 OUT PULONG ReturnedProtect
,
1310 PMMPTE PointerPte
, ProtoPte
;
1312 MMPTE TempPte
, TempProtoPte
;
1313 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1314 ULONG State
= MEM_RESERVE
, Protect
= 0;
1315 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1316 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1318 /* Only normal VADs supported */
1319 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1321 /* Get the PDE and PTE for the address */
1322 PointerPde
= MiAddressToPde(Va
);
1323 PointerPte
= MiAddressToPte(Va
);
1325 /* Return the next range */
1326 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1328 /* Is the PDE demand-zero? */
1329 if (PointerPde
->u
.Long
!= 0)
1331 /* It is not. Is it valid? */
1332 if (PointerPde
->u
.Hard
.Valid
== 0)
1334 /* Is isn't, fault it in */
1335 PointerPte
= MiPteToAddress(PointerPde
);
1336 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1342 /* It is, skip it and move to the next PDE */
1343 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1346 /* Is it safe to try reading the PTE? */
1349 /* FIXME: watch out for large pages */
1350 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1352 /* Capture the PTE */
1353 TempPte
= *PointerPte
;
1354 if (TempPte
.u
.Long
!= 0)
1356 /* The PTE is valid, so it's not zeroed out */
1357 DemandZeroPte
= FALSE
;
1359 /* Is it a decommited, invalid, or faulted PTE? */
1360 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1361 (TempPte
.u
.Hard
.Valid
== 0) &&
1362 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1363 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1365 /* Otherwise our defaults should hold */
1366 ASSERT(Protect
== 0);
1367 ASSERT(State
== MEM_RESERVE
);
1371 /* This means it's committed */
1374 /* We don't support these */
1375 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1376 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1377 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1379 /* Get protection state of this page */
1380 Protect
= MiGetPageProtection(PointerPte
);
1382 /* Check if this is an image-backed VAD */
1383 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1384 (TempPte
.u
.Soft
.Prototype
== 1) &&
1385 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1388 DPRINT1("Not supported\n");
1395 /* Check if this was a demand-zero PTE, since we need to find the state */
1398 /* Not yet handled */
1399 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1400 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1402 /* Check if this is private commited memory, or an section-backed VAD */
1403 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1405 /* Tell caller about the next range */
1406 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1408 /* Get the prototype PTE for this VAD */
1409 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1410 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1413 /* We should unlock the working set, but it's not being held! */
1415 /* Is the prototype PTE actually valid (committed)? */
1416 TempProtoPte
= *ProtoPte
;
1417 if (TempProtoPte
.u
.Long
)
1419 /* Unless this is a memory-mapped file, handle it like private VAD */
1421 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1422 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1425 /* We should re-lock the working set */
1428 else if (Vad
->u
.VadFlags
.MemCommit
)
1430 /* This is committed memory */
1433 /* Convert the protection */
1434 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1438 /* Return the protection code */
1439 *ReturnedProtect
= Protect
;
1445 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1446 IN PVOID BaseAddress
,
1447 OUT PVOID MemoryInformation
,
1448 IN SIZE_T MemoryInformationLength
,
1449 OUT PSIZE_T ReturnLength
)
1451 PEPROCESS TargetProcess
;
1452 NTSTATUS Status
= STATUS_SUCCESS
;
1454 PVOID Address
, NextAddress
;
1455 BOOLEAN Found
= FALSE
;
1456 ULONG NewProtect
, NewState
;
1458 MEMORY_BASIC_INFORMATION MemoryInfo
;
1459 KAPC_STATE ApcState
;
1460 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1461 PMEMORY_AREA MemoryArea
;
1462 SIZE_T ResultLength
;
1464 /* Check for illegal addresses in user-space, or the shared memory area */
1465 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1466 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1468 Address
= PAGE_ALIGN(BaseAddress
);
1470 /* Make up an info structure describing this range */
1471 MemoryInfo
.BaseAddress
= Address
;
1472 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1473 MemoryInfo
.Type
= MEM_PRIVATE
;
1475 /* Special case for shared data */
1476 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1478 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1479 MemoryInfo
.State
= MEM_COMMIT
;
1480 MemoryInfo
.Protect
= PAGE_READONLY
;
1481 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1485 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1486 MemoryInfo
.State
= MEM_RESERVE
;
1487 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1488 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1491 /* Return the data, NtQueryInformation already probed it*/
1492 if (PreviousMode
!= KernelMode
)
1496 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1497 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1501 Status
= _SEH2_GetExceptionCode();
1507 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1508 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1514 /* Check if this is for a local or remote process */
1515 if (ProcessHandle
== NtCurrentProcess())
1517 TargetProcess
= PsGetCurrentProcess();
1521 /* Reference the target process */
1522 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1523 PROCESS_QUERY_INFORMATION
,
1525 ExGetPreviousMode(),
1526 (PVOID
*)&TargetProcess
,
1528 if (!NT_SUCCESS(Status
)) return Status
;
1530 /* Attach to it now */
1531 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1534 /* Lock the address space and make sure the process isn't already dead */
1535 MmLockAddressSpace(&TargetProcess
->Vm
);
1536 if (TargetProcess
->VmDeleted
)
1538 /* Unlock the address space of the process */
1539 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1541 /* Check if we were attached */
1542 if (ProcessHandle
!= NtCurrentProcess())
1544 /* Detach and dereference the process */
1545 KeUnstackDetachProcess(&ApcState
);
1546 ObDereferenceObject(TargetProcess
);
1550 DPRINT1("Process is dying\n");
1551 return STATUS_PROCESS_IS_TERMINATING
;
1555 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1556 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1558 /* Scan on the right */
1559 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1560 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1563 /* Check if this VAD covers the allocation range */
1564 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1565 (BaseVpn
<= Vad
->EndingVpn
))
1572 /* Check if this VAD is too high */
1573 if (BaseVpn
< Vad
->StartingVpn
)
1575 /* Stop if there is no left child */
1576 if (!Vad
->LeftChild
) break;
1578 /* Search on the left next */
1579 Vad
= Vad
->LeftChild
;
1583 /* Then this VAD is too low, keep searching on the right */
1584 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1586 /* Stop if there is no right child */
1587 if (!Vad
->RightChild
) break;
1589 /* Search on the right next */
1590 Vad
= Vad
->RightChild
;
1595 /* Was a VAD found? */
1598 Address
= PAGE_ALIGN(BaseAddress
);
1600 /* Calculate region size */
1603 if (Vad
->StartingVpn
>= BaseVpn
)
1605 /* Region size is the free space till the start of that VAD */
1606 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1610 /* Get the next VAD */
1611 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1614 /* Region size is the free space till the start of that VAD */
1615 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1619 /* Maximum possible region size with that base address */
1620 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1626 /* Maximum possible region size with that base address */
1627 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1630 /* Unlock the address space of the process */
1631 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1633 /* Check if we were attached */
1634 if (ProcessHandle
!= NtCurrentProcess())
1636 /* Detach and derefernece the process */
1637 KeUnstackDetachProcess(&ApcState
);
1638 ObDereferenceObject(TargetProcess
);
1641 /* Build the rest of the initial information block */
1642 MemoryInfo
.BaseAddress
= Address
;
1643 MemoryInfo
.AllocationBase
= NULL
;
1644 MemoryInfo
.AllocationProtect
= 0;
1645 MemoryInfo
.State
= MEM_FREE
;
1646 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1647 MemoryInfo
.Type
= 0;
1649 /* Return the data, NtQueryInformation already probed it*/
1650 if (PreviousMode
!= KernelMode
)
1654 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1655 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1659 Status
= _SEH2_GetExceptionCode();
1665 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1666 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1672 /* Set the correct memory type based on what kind of VAD this is */
1673 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1674 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1676 MemoryInfo
.Type
= MEM_PRIVATE
;
1678 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1680 MemoryInfo
.Type
= MEM_IMAGE
;
1684 MemoryInfo
.Type
= MEM_MAPPED
;
1687 /* Find the memory area the specified address belongs to */
1688 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1689 ASSERT(MemoryArea
!= NULL
);
1691 /* Determine information dependent on the memory area type */
1692 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1694 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1695 if (!NT_SUCCESS(Status
))
1697 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1698 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1699 NT_ASSERT(NT_SUCCESS(Status
));
1704 /* Build the initial information block */
1705 Address
= PAGE_ALIGN(BaseAddress
);
1706 MemoryInfo
.BaseAddress
= Address
;
1707 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1708 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1709 MemoryInfo
.Type
= MEM_PRIVATE
;
1711 /* Find the largest chunk of memory which has the same state and protection mask */
1712 MemoryInfo
.State
= MiQueryAddressState(Address
,
1715 &MemoryInfo
.Protect
,
1717 Address
= NextAddress
;
1718 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1720 /* Keep going unless the state or protection mask changed */
1721 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1722 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1723 Address
= NextAddress
;
1726 /* Now that we know the last VA address, calculate the region size */
1727 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1730 /* Unlock the address space of the process */
1731 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1733 /* Check if we were attached */
1734 if (ProcessHandle
!= NtCurrentProcess())
1736 /* Detach and derefernece the process */
1737 KeUnstackDetachProcess(&ApcState
);
1738 ObDereferenceObject(TargetProcess
);
1741 /* Return the data, NtQueryInformation already probed it */
1742 if (PreviousMode
!= KernelMode
)
1746 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1747 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1751 Status
= _SEH2_GetExceptionCode();
1757 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1758 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1762 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1763 "State: %lx Type: %lx Size: %lx\n",
1764 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1765 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1766 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1773 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1774 IN ULONG_PTR EndingAddress
,
1776 IN PEPROCESS Process
)
1778 PMMPTE PointerPte
, LastPte
, PointerPde
;
1779 BOOLEAN OnBoundary
= TRUE
;
1782 /* Get the PDE and PTE addresses */
1783 PointerPde
= MiAddressToPde(StartingAddress
);
1784 PointerPte
= MiAddressToPte(StartingAddress
);
1785 LastPte
= MiAddressToPte(EndingAddress
);
1787 /* Loop all the PTEs */
1788 while (PointerPte
<= LastPte
)
1790 /* Check if we've hit an new PDE boundary */
1793 /* Is this PDE demand zero? */
1794 PointerPde
= MiAddressToPte(PointerPte
);
1795 if (PointerPde
->u
.Long
!= 0)
1797 /* It isn't -- is it valid? */
1798 if (PointerPde
->u
.Hard
.Valid
== 0)
1800 /* Nope, fault it in */
1801 PointerPte
= MiPteToAddress(PointerPde
);
1802 MiMakeSystemAddressValid(PointerPte
, Process
);
1807 /* The PTE was already valid, so move to the next one */
1809 PointerPte
= MiPteToAddress(PointerPde
);
1811 /* Is the entire VAD committed? If not, fail */
1812 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1814 /* Everything is committed so far past the range, return true */
1815 if (PointerPte
> LastPte
) return TRUE
;
1819 /* Is the PTE demand zero? */
1820 if (PointerPte
->u
.Long
== 0)
1822 /* Is the entire VAD committed? If not, fail */
1823 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1827 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1828 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1829 (PointerPte
->u
.Hard
.Valid
== 0) &&
1830 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1831 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1833 /* Then part of the range is decommitted, so fail */
1838 /* Move to the next PTE */
1840 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1843 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1849 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1850 IN OUT PVOID
*BaseAddress
,
1851 IN OUT PSIZE_T NumberOfBytesToProtect
,
1852 IN ULONG NewAccessProtection
,
1853 OUT PULONG OldAccessProtection OPTIONAL
)
1855 PMEMORY_AREA MemoryArea
;
1856 PMMSUPPORT AddressSpace
;
1857 ULONG OldAccessProtection_
;
1860 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1861 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1863 AddressSpace
= &Process
->Vm
;
1864 MmLockAddressSpace(AddressSpace
);
1865 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
1866 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
1868 MmUnlockAddressSpace(AddressSpace
);
1869 return STATUS_UNSUCCESSFUL
;
1872 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
1874 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
1875 Status
= MmProtectSectionView(AddressSpace
,
1878 *NumberOfBytesToProtect
,
1879 NewAccessProtection
,
1880 OldAccessProtection
);
1882 MmUnlockAddressSpace(AddressSpace
);
1889 MiProtectVirtualMemory(IN PEPROCESS Process
,
1890 IN OUT PVOID
*BaseAddress
,
1891 IN OUT PSIZE_T NumberOfBytesToProtect
,
1892 IN ULONG NewAccessProtection
,
1893 OUT PULONG OldAccessProtection OPTIONAL
)
1895 PMEMORY_AREA MemoryArea
;
1897 PMMSUPPORT AddressSpace
;
1898 ULONG_PTR StartingAddress
, EndingAddress
;
1899 PMMPTE PointerPde
, PointerPte
, LastPte
;
1902 ULONG ProtectionMask
, OldProtect
;
1904 NTSTATUS Status
= STATUS_SUCCESS
;
1905 PETHREAD Thread
= PsGetCurrentThread();
1907 /* Calculate base address for the VAD */
1908 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1909 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1911 /* Calculate the protection mask and make sure it's valid */
1912 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1913 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1915 DPRINT1("Invalid protection mask\n");
1916 return STATUS_INVALID_PAGE_PROTECTION
;
1919 /* Check for ROS specific memory area */
1920 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1921 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1924 return MiRosProtectVirtualMemory(Process
,
1926 NumberOfBytesToProtect
,
1927 NewAccessProtection
,
1928 OldAccessProtection
);
1931 /* Lock the address space and make sure the process isn't already dead */
1932 AddressSpace
= MmGetCurrentAddressSpace();
1933 MmLockAddressSpace(AddressSpace
);
1934 if (Process
->VmDeleted
)
1936 DPRINT1("Process is dying\n");
1937 Status
= STATUS_PROCESS_IS_TERMINATING
;
1941 /* Get the VAD for this address range, and make sure it exists */
1942 Vad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
1943 EndingAddress
>> PAGE_SHIFT
,
1947 DPRINT("Could not find a VAD for this allocation\n");
1948 Status
= STATUS_CONFLICTING_ADDRESSES
;
1952 /* Make sure the address is within this VAD's boundaries */
1953 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
1954 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
1956 Status
= STATUS_CONFLICTING_ADDRESSES
;
1960 /* These kinds of VADs are not supported atm */
1961 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
1962 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
1963 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
1965 DPRINT1("Illegal VAD for attempting to set protection\n");
1966 Status
= STATUS_CONFLICTING_ADDRESSES
;
1970 /* Check for a VAD whose protection can't be changed */
1971 if (Vad
->u
.VadFlags
.NoChange
== 1)
1973 DPRINT1("Trying to change protection of a NoChange VAD\n");
1974 Status
= STATUS_INVALID_PAGE_PROTECTION
;
1978 /* Is this section, or private memory? */
1979 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
1981 /* Not yet supported */
1982 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
1984 DPRINT1("Illegal VAD for attempting to set protection\n");
1985 Status
= STATUS_CONFLICTING_ADDRESSES
;
1989 /* Rotate VADs are not yet supported */
1990 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
1992 DPRINT1("Illegal VAD for attempting to set protection\n");
1993 Status
= STATUS_CONFLICTING_ADDRESSES
;
1997 /* Not valid on section files */
1998 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2001 DPRINT1("Invalid protection flags for section\n");
2002 Status
= STATUS_INVALID_PARAMETER_4
;
2006 /* Check if data or page file mapping protection PTE is compatible */
2007 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2010 DPRINT1("Fixme: Not checking for valid protection\n");
2013 /* This is a section, and this is not yet supported */
2014 DPRINT1("Section protection not yet supported\n");
2019 /* Private memory, check protection flags */
2020 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2021 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2023 DPRINT1("Invalid protection flags for private memory\n");
2024 Status
= STATUS_INVALID_PARAMETER_4
;
2028 /* Lock the working set */
2029 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2031 /* Check if all pages in this range are committed */
2032 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2039 DPRINT1("The entire range is not committed\n");
2040 Status
= STATUS_NOT_COMMITTED
;
2041 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2045 /* Compute starting and ending PTE and PDE addresses */
2046 PointerPde
= MiAddressToPde(StartingAddress
);
2047 PointerPte
= MiAddressToPte(StartingAddress
);
2048 LastPte
= MiAddressToPte(EndingAddress
);
2050 /* Make this PDE valid */
2051 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2053 /* Save protection of the first page */
2054 if (PointerPte
->u
.Long
!= 0)
2056 /* Capture the page protection and make the PDE valid */
2057 OldProtect
= MiGetPageProtection(PointerPte
);
2058 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2062 /* Grab the old protection from the VAD itself */
2063 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2066 /* Loop all the PTEs now */
2067 while (PointerPte
<= LastPte
)
2069 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2070 if (MiIsPteOnPdeBoundary(PointerPte
))
2072 PointerPde
= MiAddressToPte(PointerPte
);
2073 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2076 /* Capture the PTE and check if it was empty */
2077 PteContents
= *PointerPte
;
2078 if (PteContents
.u
.Long
== 0)
2080 /* This used to be a zero PTE and it no longer is, so we must add a
2081 reference to the pagetable. */
2082 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2085 /* Check what kind of PTE we are dealing with */
2086 if (PteContents
.u
.Hard
.Valid
== 1)
2088 /* Get the PFN entry */
2089 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2091 /* We don't support this yet */
2092 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2094 /* Check if the page should not be accessible at all */
2095 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2096 (NewAccessProtection
& PAGE_GUARD
))
2098 /* The page should be in the WS and we should make it transition now */
2099 DPRINT1("Making valid page invalid is not yet supported!\n");
2100 Status
= STATUS_NOT_IMPLEMENTED
;
2101 /* Unlock the working set */
2102 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2106 /* Write the protection mask and write it with a TLB flush */
2107 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2108 MiFlushTbAndCapture(Vad
,
2116 /* We don't support these cases yet */
2117 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2118 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2120 /* The PTE is already demand-zero, just update the protection mask */
2121 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2122 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2123 ASSERT(PointerPte
->u
.Long
!= 0);
2126 /* Move to the next PTE */
2130 /* Unlock the working set */
2131 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2134 /* Unlock the address space */
2135 MmUnlockAddressSpace(AddressSpace
);
2137 /* Return parameters and success */
2138 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2139 *BaseAddress
= (PVOID
)StartingAddress
;
2140 *OldAccessProtection
= OldProtect
;
2141 return STATUS_SUCCESS
;
2144 /* Unlock the address space and return the failure code */
2145 MmUnlockAddressSpace(AddressSpace
);
2151 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2152 IN PEPROCESS TargetProcess
,
2155 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2158 // Sanity checks. The latter is because we only use this function with the
2159 // PFN lock not held, so it may go away in the future.
2161 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2162 ASSERT(OldIrql
== MM_NOIRQL
);
2165 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2166 // return the same address as the PDE on 2-level page table systems.
2168 // If everything is already valid, there is nothing to do.
2170 PointerPpe
= MiAddressToPte(PointerPde
);
2171 PointerPxe
= MiAddressToPde(PointerPde
);
2172 if ((PointerPxe
->u
.Hard
.Valid
) &&
2173 (PointerPpe
->u
.Hard
.Valid
) &&
2174 (PointerPde
->u
.Hard
.Valid
))
2180 // At least something is invalid, so begin by getting the PTE for the PDE itself
2181 // and then lookup each additional level. We must do it in this precise order
2182 // because the pagfault.c code (as well as in Windows) depends that the next
2183 // level up (higher) must be valid when faulting a lower level
2185 PointerPte
= MiPteToAddress(PointerPde
);
2189 // Make sure APCs continued to be disabled
2191 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2194 // First, make the PXE valid if needed
2196 if (!PointerPxe
->u
.Hard
.Valid
)
2198 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2199 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2205 if (!PointerPpe
->u
.Hard
.Valid
)
2207 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2208 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2212 // And finally, make the PDE itself valid.
2214 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2217 // This should've worked the first time so the loop is really just for
2218 // show -- ASSERT that we're actually NOT going to be looping.
2220 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2221 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2222 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2223 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2224 !(PointerPpe
->u
.Hard
.Valid
) ||
2225 !(PointerPde
->u
.Hard
.Valid
));
2230 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2236 PFN_NUMBER PageFrameIndex
;
2240 // Acquire the PFN lock and loop all the PTEs in the list
2242 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2243 for (i
= 0; i
!= Count
; i
++)
2246 // The PTE must currently be valid
2248 TempPte
= *ValidPteList
[i
];
2249 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2252 // Get the PFN entry for the page itself, and then for its page table
2254 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2255 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2256 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2259 // Decrement the share count on the page table, and then on the page
2262 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2263 MI_SET_PFN_DELETED(Pfn1
);
2264 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2267 // Make the page decommitted
2269 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2273 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2274 // and then release the PFN lock
2277 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2282 MiDecommitPages(IN PVOID StartingAddress
,
2283 IN PMMPTE EndingPte
,
2284 IN PEPROCESS Process
,
2287 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2288 ULONG CommitReduction
= 0;
2289 PMMPTE ValidPteList
[256];
2293 PETHREAD CurrentThread
= PsGetCurrentThread();
2296 // Get the PTE and PTE for the address, and lock the working set
2297 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2298 // commited range ends so that we can do the right accounting.
2300 PointerPde
= MiAddressToPde(StartingAddress
);
2301 PointerPte
= MiAddressToPte(StartingAddress
);
2302 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2303 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2306 // Make the PDE valid, and now loop through each page's worth of data
2308 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2309 while (PointerPte
<= EndingPte
)
2312 // Check if we've crossed a PDE boundary
2314 if (MiIsPteOnPdeBoundary(PointerPte
))
2317 // Get the new PDE and flush the valid PTEs we had built up until
2318 // now. This helps reduce the amount of TLB flushing we have to do.
2319 // Note that Windows does a much better job using timestamps and
2320 // such, and does not flush the entire TLB all the time, but right
2321 // now we have bigger problems to worry about than TLB flushing.
2323 PointerPde
= MiAddressToPde(StartingAddress
);
2326 MiProcessValidPteList(ValidPteList
, PteCount
);
2331 // Make this PDE valid
2333 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2337 // Read this PTE. It might be active or still demand-zero.
2339 PteContents
= *PointerPte
;
2340 if (PteContents
.u
.Long
)
2343 // The PTE is active. It might be valid and in a working set, or
2344 // it might be a prototype PTE or paged out or even in transition.
2346 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2349 // It's already decommited, so there's nothing for us to do here
2356 // Remove it from the counters, and check if it was valid or not
2358 //Process->NumberOfPrivatePages--;
2359 if (PteContents
.u
.Hard
.Valid
)
2362 // It's valid. At this point make sure that it is not a ROS
2363 // PFN. Also, we don't support ProtoPTEs in this code path.
2365 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2366 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2367 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2370 // Flush any pending PTEs that we had not yet flushed, if our
2371 // list has gotten too big, then add this PTE to the flush list.
2373 if (PteCount
== 256)
2375 MiProcessValidPteList(ValidPteList
, PteCount
);
2378 ValidPteList
[PteCount
++] = PointerPte
;
2383 // We do not support any of these other scenarios at the moment
2385 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2386 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2387 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2390 // So the only other possibility is that it is still a demand
2391 // zero PTE, in which case we undo the accounting we did
2392 // earlier and simply make the page decommitted.
2394 //Process->NumberOfPrivatePages++;
2395 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2402 // This used to be a zero PTE and it no longer is, so we must add a
2403 // reference to the pagetable.
2405 MiIncrementPageTableReferences(StartingAddress
);
2408 // Next, we account for decommitted PTEs and make the PTE as such
2410 if (PointerPte
> CommitPte
) CommitReduction
++;
2411 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2415 // Move to the next PTE and the next address
2418 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2422 // Flush any dangling PTEs from the loop in the last page table, and then
2423 // release the working set and return the commit reduction accounting.
2425 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2426 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2427 return CommitReduction
;
2430 /* PUBLIC FUNCTIONS ***********************************************************/
2437 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2448 MmSecureVirtualMemory(IN PVOID Address
,
2452 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2461 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2463 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2466 /* SYSTEM CALLS ***************************************************************/
2470 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2471 IN PVOID BaseAddress
,
2473 IN SIZE_T NumberOfBytesToRead
,
2474 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2476 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2478 NTSTATUS Status
= STATUS_SUCCESS
;
2479 SIZE_T BytesRead
= 0;
2483 // Check if we came from user mode
2485 if (PreviousMode
!= KernelMode
)
2488 // Validate the read addresses
2490 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2491 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2492 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2493 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2496 // Don't allow to write into kernel space
2498 return STATUS_ACCESS_VIOLATION
;
2502 // Enter SEH for probe
2507 // Probe the output value
2509 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2511 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2514 // Get exception code
2516 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2522 // Don't do zero-byte transfers
2524 if (NumberOfBytesToRead
)
2527 // Reference the process
2529 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2535 if (NT_SUCCESS(Status
))
2540 Status
= MmCopyVirtualMemory(Process
,
2542 PsGetCurrentProcess(),
2544 NumberOfBytesToRead
,
2549 // Dereference the process
2551 ObDereferenceObject(Process
);
2556 // Check if the caller sent this parameter
2558 if (NumberOfBytesRead
)
2561 // Enter SEH to guard write
2566 // Return the number of bytes read
2568 *NumberOfBytesRead
= BytesRead
;
2570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2584 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2585 IN PVOID BaseAddress
,
2587 IN SIZE_T NumberOfBytesToWrite
,
2588 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2590 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2592 NTSTATUS Status
= STATUS_SUCCESS
;
2593 SIZE_T BytesWritten
= 0;
2597 // Check if we came from user mode
2599 if (PreviousMode
!= KernelMode
)
2602 // Validate the read addresses
2604 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2605 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2606 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2607 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2610 // Don't allow to write into kernel space
2612 return STATUS_ACCESS_VIOLATION
;
2616 // Enter SEH for probe
2621 // Probe the output value
2623 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2628 // Get exception code
2630 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2636 // Don't do zero-byte transfers
2638 if (NumberOfBytesToWrite
)
2641 // Reference the process
2643 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2649 if (NT_SUCCESS(Status
))
2654 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2658 NumberOfBytesToWrite
,
2663 // Dereference the process
2665 ObDereferenceObject(Process
);
2670 // Check if the caller sent this parameter
2672 if (NumberOfBytesWritten
)
2675 // Enter SEH to guard write
2680 // Return the number of bytes written
2682 *NumberOfBytesWritten
= BytesWritten
;
2684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2698 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2699 IN OUT PVOID
*UnsafeBaseAddress
,
2700 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2701 IN ULONG NewAccessProtection
,
2702 OUT PULONG UnsafeOldAccessProtection
)
2705 ULONG OldAccessProtection
;
2707 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2708 PVOID BaseAddress
= NULL
;
2709 SIZE_T NumberOfBytesToProtect
= 0;
2710 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2712 BOOLEAN Attached
= FALSE
;
2713 KAPC_STATE ApcState
;
2717 // Check for valid protection flags
2719 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2720 if (Protection
!= PAGE_NOACCESS
&&
2721 Protection
!= PAGE_READONLY
&&
2722 Protection
!= PAGE_READWRITE
&&
2723 Protection
!= PAGE_WRITECOPY
&&
2724 Protection
!= PAGE_EXECUTE
&&
2725 Protection
!= PAGE_EXECUTE_READ
&&
2726 Protection
!= PAGE_EXECUTE_READWRITE
&&
2727 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2732 return STATUS_INVALID_PAGE_PROTECTION
;
2736 // Check if we came from user mode
2738 if (PreviousMode
!= KernelMode
)
2741 // Enter SEH for probing
2746 // Validate all outputs
2748 ProbeForWritePointer(UnsafeBaseAddress
);
2749 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2750 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2755 BaseAddress
= *UnsafeBaseAddress
;
2756 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2758 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2761 // Get exception code
2763 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2772 BaseAddress
= *UnsafeBaseAddress
;
2773 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2777 // Catch illegal base address
2779 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2782 // Catch illegal region size
2784 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2789 return STATUS_INVALID_PARAMETER_3
;
2793 // 0 is also illegal
2795 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2798 // Get a reference to the process
2800 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2801 PROCESS_VM_OPERATION
,
2806 if (!NT_SUCCESS(Status
)) return Status
;
2809 // Check if we should attach
2811 if (CurrentProcess
!= Process
)
2816 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2821 // Do the actual work
2823 Status
= MiProtectVirtualMemory(Process
,
2825 &NumberOfBytesToProtect
,
2826 NewAccessProtection
,
2827 &OldAccessProtection
);
2832 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2835 // Release reference
2837 ObDereferenceObject(Process
);
2840 // Enter SEH to return data
2845 // Return data to user
2847 *UnsafeOldAccessProtection
= OldAccessProtection
;
2848 *UnsafeBaseAddress
= BaseAddress
;
2849 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2868 // HACK until we have proper WSLIST support
2869 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2871 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
2873 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
2885 // HACK until we have proper WSLIST support
2886 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2888 if (!Wsle
->u1
.e1
.LockedInWs
&&
2889 !Wsle
->u1
.e1
.LockedInMemory
)
2891 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
2894 if (LockType
& MAP_PROCESS
)
2895 Wsle
->u1
.e1
.LockedInWs
= 1;
2896 if (LockType
& MAP_SYSTEM
)
2897 Wsle
->u1
.e1
.LockedInMemory
= 1;
2906 // HACK until we have proper WSLIST support
2907 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2909 if (LockType
& MAP_PROCESS
)
2910 Wsle
->u1
.e1
.LockedInWs
= 0;
2911 if (LockType
& MAP_SYSTEM
)
2912 Wsle
->u1
.e1
.LockedInMemory
= 0;
2914 if (!Wsle
->u1
.e1
.LockedInWs
&&
2915 !Wsle
->u1
.e1
.LockedInMemory
)
2917 MiDereferencePfnAndDropLockCount(Pfn1
);
2923 MiCheckVadsForLockOperation(
2924 _Inout_ PVOID
*BaseAddress
,
2925 _Inout_ PSIZE_T RegionSize
,
2926 _Inout_ PVOID
*EndAddress
)
2932 /* Get the base address and align the start address */
2933 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
2934 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
2935 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
2937 /* First loop and check all VADs */
2938 CurrentVa
= *BaseAddress
;
2939 while (CurrentVa
< *EndAddress
)
2942 Vad
= MiLocateAddress(CurrentVa
);
2945 /// FIXME: this might be a memory area for a section view...
2946 return STATUS_ACCESS_VIOLATION
;
2949 /* Check VAD type */
2950 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
2951 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
2952 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
2954 *EndAddress
= CurrentVa
;
2955 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
2956 return STATUS_INCOMPATIBLE_FILE_MAP
;
2959 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
2962 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
2963 return STATUS_SUCCESS
;
2968 MiLockVirtualMemory(
2969 IN OUT PVOID
*BaseAddress
,
2970 IN OUT PSIZE_T RegionSize
,
2973 PEPROCESS CurrentProcess
;
2974 PMMSUPPORT AddressSpace
;
2975 PVOID CurrentVa
, EndAddress
;
2976 PMMPTE PointerPte
, LastPte
;
2978 #if (_MI_PAGING_LEVELS >= 3)
2981 #if (_MI_PAGING_LEVELS == 4)
2985 NTSTATUS Status
, TempStatus
;
2987 /* Lock the address space */
2988 AddressSpace
= MmGetCurrentAddressSpace();
2989 MmLockAddressSpace(AddressSpace
);
2991 /* Make sure we still have an address space */
2992 CurrentProcess
= PsGetCurrentProcess();
2993 if (CurrentProcess
->VmDeleted
)
2995 Status
= STATUS_PROCESS_IS_TERMINATING
;
2999 /* Check the VADs in the requested range */
3000 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3001 if (!NT_SUCCESS(Status
))
3006 /* Enter SEH for probing */
3009 /* Loop all pages and probe them */
3010 CurrentVa
= *BaseAddress
;
3011 while (CurrentVa
< EndAddress
)
3013 (void)(*(volatile CHAR
*)CurrentVa
);
3014 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3017 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3019 Status
= _SEH2_GetExceptionCode();
3024 /* All pages were accessible, since we hold the address space lock, nothing
3025 can be de-committed. Assume success for now. */
3026 Status
= STATUS_SUCCESS
;
3028 /* Get the PTE and PDE */
3029 PointerPte
= MiAddressToPte(*BaseAddress
);
3030 PointerPde
= MiAddressToPde(*BaseAddress
);
3031 #if (_MI_PAGING_LEVELS >= 3)
3032 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3034 #if (_MI_PAGING_LEVELS == 4)
3035 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3038 /* Get the last PTE */
3039 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3041 /* Lock the process working set */
3042 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3044 /* Loop the pages */
3047 /* Check for a page that is not accessible */
3049 #if (_MI_PAGING_LEVELS == 4)
3050 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3052 #if (_MI_PAGING_LEVELS >= 3)
3053 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3055 (PointerPde
->u
.Hard
.Valid
== 0) ||
3056 (PointerPte
->u
.Hard
.Valid
== 0))
3058 /* Release process working set */
3059 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3061 /* Access the page */
3062 CurrentVa
= MiPteToAddress(PointerPte
);
3064 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3065 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3066 if (!NT_SUCCESS(TempStatus
))
3068 // This should only happen, when remote backing storage is not accessible
3070 Status
= TempStatus
;
3074 /* Lock the process working set */
3075 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3079 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3080 ASSERT(Pfn1
!= NULL
);
3082 /* Check the previous lock status */
3083 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3085 Status
= STATUS_WAS_LOCKED
;
3089 MI_LOCK_VA(Pfn1
, MapType
);
3091 /* Go to the next PTE */
3094 /* Check if we're on a PDE boundary */
3095 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3096 #if (_MI_PAGING_LEVELS >= 3)
3097 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3099 #if (_MI_PAGING_LEVELS == 4)
3100 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3102 } while (PointerPte
<= LastPte
);
3104 /* Release process working set */
3105 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3108 /* Unlock address space */
3109 MmUnlockAddressSpace(AddressSpace
);
3116 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3117 IN OUT PVOID
*BaseAddress
,
3118 IN OUT PSIZE_T NumberOfBytesToLock
,
3122 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3124 BOOLEAN Attached
= FALSE
;
3125 KAPC_STATE ApcState
;
3126 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3127 PVOID CapturedBaseAddress
;
3128 SIZE_T CapturedBytesToLock
;
3134 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3137 // Invalid set of flags
3139 return STATUS_INVALID_PARAMETER
;
3143 // At least one flag must be specified
3145 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3150 return STATUS_INVALID_PARAMETER
;
3154 // Enter SEH for probing
3159 // Validate output data
3161 ProbeForWritePointer(BaseAddress
);
3162 ProbeForWriteSize_t(NumberOfBytesToLock
);
3167 CapturedBaseAddress
= *BaseAddress
;
3168 CapturedBytesToLock
= *NumberOfBytesToLock
;
3170 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3173 // Get exception code
3175 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3180 // Catch illegal base address
3182 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3185 // Catch illegal region size
3187 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3192 return STATUS_INVALID_PARAMETER
;
3196 // 0 is also illegal
3198 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3201 // Get a reference to the process
3203 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3204 PROCESS_VM_OPERATION
,
3209 if (!NT_SUCCESS(Status
)) return Status
;
3212 // Check if this is is system-mapped
3214 if (MapType
& MAP_SYSTEM
)
3217 // Check for required privilege
3219 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3222 // Fail: Don't have it
3224 ObDereferenceObject(Process
);
3225 return STATUS_PRIVILEGE_NOT_HELD
;
3230 // Check if we should attach
3232 if (CurrentProcess
!= Process
)
3237 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3242 // Call the internal function
3244 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3245 &CapturedBytesToLock
,
3251 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3254 // Release reference
3256 ObDereferenceObject(Process
);
3259 // Enter SEH to return data
3264 // Return data to user
3266 *BaseAddress
= CapturedBaseAddress
;
3267 *NumberOfBytesToLock
= CapturedBytesToLock
;
3269 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3272 // Get exception code
3274 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3287 MiUnlockVirtualMemory(
3288 IN OUT PVOID
*BaseAddress
,
3289 IN OUT PSIZE_T RegionSize
,
3292 PEPROCESS CurrentProcess
;
3293 PMMSUPPORT AddressSpace
;
3295 PMMPTE PointerPte
, LastPte
;
3297 #if (_MI_PAGING_LEVELS >= 3)
3300 #if (_MI_PAGING_LEVELS == 4)
3306 /* Lock the address space */
3307 AddressSpace
= MmGetCurrentAddressSpace();
3308 MmLockAddressSpace(AddressSpace
);
3310 /* Make sure we still have an address space */
3311 CurrentProcess
= PsGetCurrentProcess();
3312 if (CurrentProcess
->VmDeleted
)
3314 Status
= STATUS_PROCESS_IS_TERMINATING
;
3318 /* Check the VADs in the requested range */
3319 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3321 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3322 incompatible VAD we continue, like Windows does */
3323 if (Status
== STATUS_ACCESS_VIOLATION
)
3325 Status
= STATUS_NOT_LOCKED
;
3329 /* Get the PTE and PDE */
3330 PointerPte
= MiAddressToPte(*BaseAddress
);
3331 PointerPde
= MiAddressToPde(*BaseAddress
);
3332 #if (_MI_PAGING_LEVELS >= 3)
3333 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3335 #if (_MI_PAGING_LEVELS == 4)
3336 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3339 /* Get the last PTE */
3340 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3342 /* Lock the process working set */
3343 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3345 /* Loop the pages */
3348 /* Check for a page that is not present */
3350 #if (_MI_PAGING_LEVELS == 4)
3351 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3353 #if (_MI_PAGING_LEVELS >= 3)
3354 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3356 (PointerPde
->u
.Hard
.Valid
== 0) ||
3357 (PointerPte
->u
.Hard
.Valid
== 0))
3359 /* Remember it, but keep going */
3360 Status
= STATUS_NOT_LOCKED
;
3365 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3366 ASSERT(Pfn1
!= NULL
);
3368 /* Check if all of the requested locks are present */
3369 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3370 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3372 /* Remember it, but keep going */
3373 Status
= STATUS_NOT_LOCKED
;
3375 /* Check if no lock is present */
3376 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3378 DPRINT1("FIXME: Should remove the page from WS\n");
3383 /* Go to the next PTE */
3386 /* Check if we're on a PDE boundary */
3387 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3388 #if (_MI_PAGING_LEVELS >= 3)
3389 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3391 #if (_MI_PAGING_LEVELS == 4)
3392 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3394 } while (PointerPte
<= LastPte
);
3396 /* Check if we hit a page that was not locked */
3397 if (Status
== STATUS_NOT_LOCKED
)
3399 goto CleanupWithWsLock
;
3402 /* All pages in the region were locked, so unlock them all */
3404 /* Get the PTE and PDE */
3405 PointerPte
= MiAddressToPte(*BaseAddress
);
3406 PointerPde
= MiAddressToPde(*BaseAddress
);
3407 #if (_MI_PAGING_LEVELS >= 3)
3408 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3410 #if (_MI_PAGING_LEVELS == 4)
3411 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3414 /* Loop the pages */
3418 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3419 MI_UNLOCK_VA(Pfn1
, MapType
);
3421 /* Go to the next PTE */
3424 /* Check if we're on a PDE boundary */
3425 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3426 #if (_MI_PAGING_LEVELS >= 3)
3427 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3429 #if (_MI_PAGING_LEVELS == 4)