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
= (ULONG
)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 /* See if the PTE is valid */
408 if (TempPte
.u
.Hard
.Valid
== 0)
410 /* Prototype PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 if (TempPte
.u
.Soft
.Transition
)
414 /* Get the PFN entry */
415 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
416 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
418 DPRINT1("Pte %p is transitional!\n", PointerPte
);
420 /* Destroy the PTE */
421 MI_ERASE_PTE(PointerPte
);
423 /* Drop the reference on the page table. */
424 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
426 if (Pfn1
->u2
.ShareCount
== 0)
428 NT_ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
429 /* Mark the page temporarily as valid, we're going to make it free soon */
430 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
432 /* Bring it back into the free list */
433 MiInsertPageInFreeList(PageFrameIndex
);
439 /* Get the PFN entry */
440 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
441 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
443 /* Check if this is a valid, prototype PTE */
444 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
446 /* Get the PDE and make sure it's faulted in */
447 PointerPde
= MiPteToPde(PointerPte
);
448 if (PointerPde
->u
.Hard
.Valid
== 0)
450 #if (_MI_PAGING_LEVELS == 2)
451 /* Could be paged pool access from a new process -- synchronize the page directories */
452 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
455 /* The PDE must be valid at this point */
456 KeBugCheckEx(MEMORY_MANAGEMENT
,
458 (ULONG_PTR
)PointerPte
,
460 (ULONG_PTR
)VirtualAddress
);
462 #if (_MI_PAGING_LEVELS == 2)
465 /* Drop the share count */
466 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
468 /* Either a fork, or this is the shared user data page */
469 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
471 /* If it's not the shared user page, then crash, since there's no fork() yet */
472 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
473 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
475 /* Must be some sort of memory corruption */
476 KeBugCheckEx(MEMORY_MANAGEMENT
,
478 (ULONG_PTR
)PointerPte
,
479 (ULONG_PTR
)PrototypePte
,
480 (ULONG_PTR
)Pfn1
->PteAddress
);
485 MI_ERASE_PTE(PointerPte
);
489 /* Make sure the saved PTE address is valid */
490 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
492 /* The PFN entry is illegal, or invalid */
493 KeBugCheckEx(MEMORY_MANAGEMENT
,
495 (ULONG_PTR
)PointerPte
,
497 (ULONG_PTR
)Pfn1
->PteAddress
);
501 MI_ERASE_PTE(PointerPte
);
503 /* There should only be 1 shared reference count */
504 ASSERT(Pfn1
->u2
.ShareCount
== 1);
506 /* Drop the reference on the page table. */
507 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
509 /* Mark the PFN for deletion and dereference what should be the last ref */
510 MI_SET_PFN_DELETED(Pfn1
);
511 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
513 /* We should eventually do this */
514 //CurrentProcess->NumberOfPrivatePages--;
523 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
524 IN ULONG_PTR EndingAddress
,
527 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
530 PEPROCESS CurrentProcess
;
532 BOOLEAN AddressGap
= FALSE
;
533 PSUBSECTION Subsection
;
535 /* Get out if this is a fake VAD, RosMm will free the marea pages */
536 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
538 /* Grab the process and PTE/PDE for the address being deleted */
539 CurrentProcess
= PsGetCurrentProcess();
540 PointerPde
= MiAddressToPde(Va
);
541 PointerPte
= MiAddressToPte(Va
);
543 /* Check if this is a section VAD or a VM VAD */
544 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
546 /* Don't worry about prototypes */
547 PrototypePte
= LastPrototypePte
= NULL
;
551 /* Get the prototype PTE */
552 PrototypePte
= Vad
->FirstPrototypePte
;
553 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
556 /* In all cases, we don't support fork() yet */
557 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
559 /* Loop the PTE for each VA */
562 /* First keep going until we find a valid PDE */
563 while (!PointerPde
->u
.Long
)
565 /* There are gaps in the address space */
568 /* Still no valid PDE, try the next 4MB (or whatever) */
571 /* Update the PTE on this new boundary */
572 PointerPte
= MiPteToAddress(PointerPde
);
574 /* Check if all the PDEs are invalid, so there's nothing to free */
575 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
576 if (Va
> EndingAddress
) return;
579 /* Now check if the PDE is mapped in */
580 if (!PointerPde
->u
.Hard
.Valid
)
582 /* It isn't, so map it in */
583 PointerPte
= MiPteToAddress(PointerPde
);
584 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
587 /* Now we should have a valid PDE, mapped in, and still have some VA */
588 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
589 ASSERT(Va
<= EndingAddress
);
591 /* Check if this is a section VAD with gaps in it */
592 if ((AddressGap
) && (LastPrototypePte
))
594 /* We need to skip to the next correct prototype PTE */
595 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
597 /* And we need the subsection to skip to the next last prototype PTE */
598 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
602 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
606 /* No more subsections, we are done with prototype PTEs */
611 /* Lock the PFN Database while we delete the PTEs */
612 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
615 /* Capture the PDE and make sure it exists */
616 TempPte
= *PointerPte
;
619 MiDecrementPageTableReferences((PVOID
)Va
);
621 /* Check if the PTE is actually mapped in */
622 if (MI_IS_MAPPED_PTE(&TempPte
))
624 /* Are we dealing with section VAD? */
625 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
627 /* We need to skip to the next correct prototype PTE */
628 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
630 /* And we need the subsection to skip to the next last prototype PTE */
631 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
635 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
639 /* No more subsections, we are done with prototype PTEs */
644 /* Check for prototype PTE */
645 if ((TempPte
.u
.Hard
.Valid
== 0) &&
646 (TempPte
.u
.Soft
.Prototype
== 1))
649 MI_ERASE_PTE(PointerPte
);
653 /* Delete the PTE proper */
654 MiDeletePte(PointerPte
,
662 /* The PTE was never mapped, just nuke it here */
663 MI_ERASE_PTE(PointerPte
);
667 /* Update the address and PTE for it */
672 /* Making sure the PDE is still valid */
673 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
675 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
677 /* The PDE should still be valid at this point */
678 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
680 /* Check remaining PTE count (go back 1 page due to above loop) */
681 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
683 if (PointerPde
->u
.Long
!= 0)
685 /* Delete the PTE proper */
686 MiDeletePte(PointerPde
,
687 MiPteToAddress(PointerPde
),
693 /* Release the lock and get out if we're done */
694 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
695 if (Va
> EndingAddress
) return;
697 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
698 PointerPde
= MiAddressToPde(Va
);
704 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
705 OUT PBOOLEAN HaveBadAddress
,
706 OUT PULONG_PTR BadAddress
)
708 PEXCEPTION_RECORD ExceptionRecord
;
714 *HaveBadAddress
= FALSE
;
717 // Get the exception record
719 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
722 // Look at the exception code
724 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
725 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
726 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
729 // We can tell the address if we have more than one parameter
731 if (ExceptionRecord
->NumberParameters
> 1)
734 // Return the address
736 *HaveBadAddress
= TRUE
;
737 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
742 // Continue executing the next handler
744 return EXCEPTION_EXECUTE_HANDLER
;
749 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
750 IN PVOID SourceAddress
,
751 IN PEPROCESS TargetProcess
,
752 OUT PVOID TargetAddress
,
753 IN SIZE_T BufferSize
,
754 IN KPROCESSOR_MODE PreviousMode
,
755 OUT PSIZE_T ReturnSize
)
757 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
758 PMDL Mdl
= (PMDL
)MdlBuffer
;
759 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
760 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
761 volatile BOOLEAN PagesLocked
;
762 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
763 volatile PVOID MdlAddress
;
765 BOOLEAN HaveBadAddress
;
766 ULONG_PTR BadAddress
;
767 NTSTATUS Status
= STATUS_SUCCESS
;
771 // Calculate the maximum amount of data to move
773 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
774 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
775 CurrentSize
= TotalSize
;
776 RemainingSize
= BufferSize
;
779 // Loop as long as there is still data
781 while (RemainingSize
> 0)
784 // Check if this transfer will finish everything off
786 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
789 // Attach to the source address space
791 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
794 // Reset state for this pass
798 FailedInMoving
= FALSE
;
799 ASSERT(FailedInProbe
== FALSE
);
802 // Protect user-mode copy
807 // If this is our first time, probe the buffer
809 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
812 // Catch a failure here
814 FailedInProbe
= TRUE
;
819 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
824 FailedInProbe
= FALSE
;
828 // Initialize and probe and lock the MDL
830 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
831 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
837 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
846 // Use our SEH handler to pick this up
848 FailedInMapping
= TRUE
;
849 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
853 // Now let go of the source and grab to the target process
855 KeUnstackDetachProcess(&ApcState
);
856 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
859 // Check if this is our first time through
861 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
864 // Catch a failure here
866 FailedInProbe
= TRUE
;
871 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
876 FailedInProbe
= FALSE
;
880 // Now do the actual move
882 FailedInMoving
= TRUE
;
883 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
885 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
890 // Detach from whoever we may be attached to
892 KeUnstackDetachProcess(&ApcState
);
895 // Check if we had mapped the pages
897 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
900 // Check if we had locked the pages
902 if (PagesLocked
) MmUnlockPages(Mdl
);
905 // Check if we hit working set quota
907 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
912 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
916 // Check if we failed during the probe or mapping
918 if ((FailedInProbe
) || (FailedInMapping
))
923 Status
= _SEH2_GetExceptionCode();
924 _SEH2_YIELD(return Status
);
928 // Otherwise, we failed probably during the move
930 *ReturnSize
= BufferSize
- RemainingSize
;
934 // Check if we know exactly where we stopped copying
939 // Return the exact number of bytes copied
941 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
946 // Return partial copy
948 Status
= STATUS_PARTIAL_COPY
;
953 // Check for SEH status
955 if (Status
!= STATUS_SUCCESS
) return Status
;
958 // Detach from target
960 KeUnstackDetachProcess(&ApcState
);
965 MmUnmapLockedPages(MdlAddress
, Mdl
);
969 // Update location and size
971 RemainingSize
-= CurrentSize
;
972 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
973 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
979 *ReturnSize
= BufferSize
;
980 return STATUS_SUCCESS
;
985 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
986 IN PVOID SourceAddress
,
987 IN PEPROCESS TargetProcess
,
988 OUT PVOID TargetAddress
,
989 IN SIZE_T BufferSize
,
990 IN KPROCESSOR_MODE PreviousMode
,
991 OUT PSIZE_T ReturnSize
)
993 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
994 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
995 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
996 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
999 BOOLEAN HaveBadAddress
;
1000 ULONG_PTR BadAddress
;
1001 NTSTATUS Status
= STATUS_SUCCESS
;
1005 // Calculate the maximum amount of data to move
1007 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1008 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1009 CurrentSize
= TotalSize
;
1010 RemainingSize
= BufferSize
;
1013 // Check if we can use the stack
1015 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1020 PoolAddress
= (PVOID
)StackBuffer
;
1027 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1028 if (!PoolAddress
) ASSERT(FALSE
);
1029 HavePoolAddress
= TRUE
;
1033 // Loop as long as there is still data
1035 while (RemainingSize
> 0)
1038 // Check if this transfer will finish everything off
1040 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1043 // Attach to the source address space
1045 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1048 // Reset state for this pass
1050 FailedInMoving
= FALSE
;
1051 ASSERT(FailedInProbe
== FALSE
);
1054 // Protect user-mode copy
1059 // If this is our first time, probe the buffer
1061 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1064 // Catch a failure here
1066 FailedInProbe
= TRUE
;
1071 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1076 FailedInProbe
= FALSE
;
1082 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1085 // Now let go of the source and grab to the target process
1087 KeUnstackDetachProcess(&ApcState
);
1088 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1091 // Check if this is our first time through
1093 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1096 // Catch a failure here
1098 FailedInProbe
= TRUE
;
1103 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1108 FailedInProbe
= FALSE
;
1112 // Now do the actual move
1114 FailedInMoving
= TRUE
;
1115 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1117 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1122 // Detach from whoever we may be attached to
1124 KeUnstackDetachProcess(&ApcState
);
1127 // Check if we had allocated pool
1129 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1132 // Check if we failed during the probe
1139 Status
= _SEH2_GetExceptionCode();
1140 _SEH2_YIELD(return Status
);
1144 // Otherwise, we failed, probably during the move
1146 *ReturnSize
= BufferSize
- RemainingSize
;
1150 // Check if we know exactly where we stopped copying
1155 // Return the exact number of bytes copied
1157 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1162 // Return partial copy
1164 Status
= STATUS_PARTIAL_COPY
;
1169 // Check for SEH status
1171 if (Status
!= STATUS_SUCCESS
) return Status
;
1174 // Detach from target
1176 KeUnstackDetachProcess(&ApcState
);
1179 // Update location and size
1181 RemainingSize
-= CurrentSize
;
1182 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1183 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1188 // Check if we had allocated pool
1190 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1195 *ReturnSize
= BufferSize
;
1196 return STATUS_SUCCESS
;
1201 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1202 IN PVOID SourceAddress
,
1203 IN PEPROCESS TargetProcess
,
1204 OUT PVOID TargetAddress
,
1205 IN SIZE_T BufferSize
,
1206 IN KPROCESSOR_MODE PreviousMode
,
1207 OUT PSIZE_T ReturnSize
)
1210 PEPROCESS Process
= SourceProcess
;
1213 // Don't accept zero-sized buffers
1215 if (!BufferSize
) return STATUS_SUCCESS
;
1218 // If we are copying from ourselves, lock the target instead
1220 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1223 // Acquire rundown protection
1225 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1230 return STATUS_PROCESS_IS_TERMINATING
;
1234 // See if we should use the pool copy
1236 if (BufferSize
> MI_POOL_COPY_BYTES
)
1241 Status
= MiDoMappedCopy(SourceProcess
,
1254 Status
= MiDoPoolCopy(SourceProcess
,
1266 ExReleaseRundownProtection(&Process
->RundownProtect
);
1272 MmFlushVirtualMemory(IN PEPROCESS Process
,
1273 IN OUT PVOID
*BaseAddress
,
1274 IN OUT PSIZE_T RegionSize
,
1275 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1283 return STATUS_SUCCESS
;
1288 MiGetPageProtection(IN PMMPTE PointerPte
)
1292 PEPROCESS CurrentProcess
;
1293 PETHREAD CurrentThread
;
1294 BOOLEAN WsSafe
, WsShared
;
1299 /* Copy this PTE's contents */
1300 TempPte
= *PointerPte
;
1302 /* Assure it's not totally zero */
1303 ASSERT(TempPte
.u
.Long
);
1305 /* Check for a special prototype format */
1306 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1307 (TempPte
.u
.Soft
.Prototype
== 1))
1309 /* Check if the prototype PTE is not yet pointing to a PTE */
1310 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1312 /* The prototype PTE contains the protection */
1313 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1316 /* Get a pointer to the underlying shared PTE */
1317 PointerPte
= MiProtoPteToPte(&TempPte
);
1319 /* Since the PTE we want to read can be paged out at any time, we need
1320 to release the working set lock first, so that it can be paged in */
1321 CurrentThread
= PsGetCurrentThread();
1322 CurrentProcess
= PsGetCurrentProcess();
1323 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1328 /* Now read the PTE value */
1329 TempPte
= *PointerPte
;
1331 /* Check if that one is invalid */
1332 if (!TempPte
.u
.Hard
.Valid
)
1334 /* We get the protection directly from this PTE */
1335 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1339 /* The PTE is valid, so we might need to get the protection from
1340 the PFN. Lock the PFN database */
1341 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1343 /* Check if the PDE is still valid */
1344 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1346 /* It's not, make it valid */
1347 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1350 /* Now it's safe to read the PTE value again */
1351 TempPte
= *PointerPte
;
1352 ASSERT(TempPte
.u
.Long
!= 0);
1354 /* Check again if the PTE is invalid */
1355 if (!TempPte
.u
.Hard
.Valid
)
1357 /* The PTE is not valid, so we can use it's protection field */
1358 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1362 /* The PTE is valid, so we can find the protection in the
1363 OriginalPte field of the PFN */
1364 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1365 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1368 /* Release the PFN database */
1369 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1372 /* Lock the working set again */
1373 MiLockProcessWorkingSetForFault(CurrentProcess
,
1381 /* In the easy case of transition or demand zero PTE just return its protection */
1382 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1384 /* If we get here, the PTE is valid, so look up the page in PFN database */
1385 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1386 if (!Pfn
->u3
.e1
.PrototypePte
)
1388 /* Return protection of the original pte */
1389 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1390 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1393 /* This is software PTE */
1394 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1395 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1396 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1397 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1398 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1403 MiQueryAddressState(IN PVOID Va
,
1405 IN PEPROCESS TargetProcess
,
1406 OUT PULONG ReturnedProtect
,
1410 PMMPTE PointerPte
, ProtoPte
;
1412 #if (_MI_PAGING_LEVELS >= 3)
1415 #if (_MI_PAGING_LEVELS >= 4)
1418 MMPTE TempPte
, TempProtoPte
;
1419 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1420 ULONG State
= MEM_RESERVE
, Protect
= 0;
1421 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1422 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1424 /* Only normal VADs supported */
1425 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1427 /* Get the PDE and PTE for the address */
1428 PointerPde
= MiAddressToPde(Va
);
1429 PointerPte
= MiAddressToPte(Va
);
1430 #if (_MI_PAGING_LEVELS >= 3)
1431 PointerPpe
= MiAddressToPpe(Va
);
1433 #if (_MI_PAGING_LEVELS >= 4)
1434 PointerPxe
= MiAddressToPxe(Va
);
1437 /* Return the next range */
1438 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1442 #if (_MI_PAGING_LEVELS >= 4)
1443 /* Does the PXE exist? */
1444 if (PointerPxe
->u
.Long
== 0)
1446 /* It does not, next range starts at the next PXE */
1447 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1451 /* Is the PXE valid? */
1452 if (PointerPxe
->u
.Hard
.Valid
== 0)
1454 /* Is isn't, fault it in (make the PPE accessible) */
1455 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1458 #if (_MI_PAGING_LEVELS >= 3)
1459 /* Does the PPE exist? */
1460 if (PointerPpe
->u
.Long
== 0)
1462 /* It does not, next range starts at the next PPE */
1463 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1467 /* Is the PPE valid? */
1468 if (PointerPpe
->u
.Hard
.Valid
== 0)
1470 /* Is isn't, fault it in (make the PDE accessible) */
1471 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1475 /* Does the PDE exist? */
1476 if (PointerPde
->u
.Long
== 0)
1478 /* It does not, next range starts at the next PDE */
1479 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1483 /* Is the PDE valid? */
1484 if (PointerPde
->u
.Hard
.Valid
== 0)
1486 /* Is isn't, fault it in (make the PTE accessible) */
1487 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1490 /* We have a PTE that we can access now! */
1495 /* Is it safe to try reading the PTE? */
1498 /* FIXME: watch out for large pages */
1499 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1501 /* Capture the PTE */
1502 TempPte
= *PointerPte
;
1503 if (TempPte
.u
.Long
!= 0)
1505 /* The PTE is valid, so it's not zeroed out */
1506 DemandZeroPte
= FALSE
;
1508 /* Is it a decommited, invalid, or faulted PTE? */
1509 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1510 (TempPte
.u
.Hard
.Valid
== 0) &&
1511 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1512 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1514 /* Otherwise our defaults should hold */
1515 ASSERT(Protect
== 0);
1516 ASSERT(State
== MEM_RESERVE
);
1520 /* This means it's committed */
1523 /* We don't support these */
1524 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1525 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1526 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1528 /* Get protection state of this page */
1529 Protect
= MiGetPageProtection(PointerPte
);
1531 /* Check if this is an image-backed VAD */
1532 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1533 (TempPte
.u
.Soft
.Prototype
== 1) &&
1534 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1537 DPRINT1("Not supported\n");
1544 /* Check if this was a demand-zero PTE, since we need to find the state */
1547 /* Not yet handled */
1548 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1549 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1551 /* Check if this is private commited memory, or an section-backed VAD */
1552 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1554 /* Tell caller about the next range */
1555 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1557 /* Get the prototype PTE for this VAD */
1558 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1559 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1562 /* We should unlock the working set, but it's not being held! */
1564 /* Is the prototype PTE actually valid (committed)? */
1565 TempProtoPte
= *ProtoPte
;
1566 if (TempProtoPte
.u
.Long
)
1568 /* Unless this is a memory-mapped file, handle it like private VAD */
1570 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1571 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1574 /* We should re-lock the working set */
1577 else if (Vad
->u
.VadFlags
.MemCommit
)
1579 /* This is committed memory */
1582 /* Convert the protection */
1583 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1587 /* Return the protection code */
1588 *ReturnedProtect
= Protect
;
1594 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1595 IN PVOID BaseAddress
,
1596 OUT PVOID MemoryInformation
,
1597 IN SIZE_T MemoryInformationLength
,
1598 OUT PSIZE_T ReturnLength
)
1600 PEPROCESS TargetProcess
;
1601 NTSTATUS Status
= STATUS_SUCCESS
;
1603 PVOID Address
, NextAddress
;
1604 BOOLEAN Found
= FALSE
;
1605 ULONG NewProtect
, NewState
;
1607 MEMORY_BASIC_INFORMATION MemoryInfo
;
1608 KAPC_STATE ApcState
;
1609 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1610 PMEMORY_AREA MemoryArea
;
1611 SIZE_T ResultLength
;
1613 /* Check for illegal addresses in user-space, or the shared memory area */
1614 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1615 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1617 Address
= PAGE_ALIGN(BaseAddress
);
1619 /* Make up an info structure describing this range */
1620 MemoryInfo
.BaseAddress
= Address
;
1621 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1622 MemoryInfo
.Type
= MEM_PRIVATE
;
1624 /* Special case for shared data */
1625 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1627 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1628 MemoryInfo
.State
= MEM_COMMIT
;
1629 MemoryInfo
.Protect
= PAGE_READONLY
;
1630 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1634 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1635 MemoryInfo
.State
= MEM_RESERVE
;
1636 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1637 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1640 /* Return the data, NtQueryInformation already probed it*/
1641 if (PreviousMode
!= KernelMode
)
1645 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1646 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1648 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1650 Status
= _SEH2_GetExceptionCode();
1656 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1657 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1663 /* Check if this is for a local or remote process */
1664 if (ProcessHandle
== NtCurrentProcess())
1666 TargetProcess
= PsGetCurrentProcess();
1670 /* Reference the target process */
1671 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1672 PROCESS_QUERY_INFORMATION
,
1674 ExGetPreviousMode(),
1675 (PVOID
*)&TargetProcess
,
1677 if (!NT_SUCCESS(Status
)) return Status
;
1679 /* Attach to it now */
1680 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1683 /* Lock the address space and make sure the process isn't already dead */
1684 MmLockAddressSpace(&TargetProcess
->Vm
);
1685 if (TargetProcess
->VmDeleted
)
1687 /* Unlock the address space of the process */
1688 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1690 /* Check if we were attached */
1691 if (ProcessHandle
!= NtCurrentProcess())
1693 /* Detach and dereference the process */
1694 KeUnstackDetachProcess(&ApcState
);
1695 ObDereferenceObject(TargetProcess
);
1699 DPRINT1("Process is dying\n");
1700 return STATUS_PROCESS_IS_TERMINATING
;
1704 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1705 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1707 /* Scan on the right */
1708 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1709 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1712 /* Check if this VAD covers the allocation range */
1713 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1714 (BaseVpn
<= Vad
->EndingVpn
))
1721 /* Check if this VAD is too high */
1722 if (BaseVpn
< Vad
->StartingVpn
)
1724 /* Stop if there is no left child */
1725 if (!Vad
->LeftChild
) break;
1727 /* Search on the left next */
1728 Vad
= Vad
->LeftChild
;
1732 /* Then this VAD is too low, keep searching on the right */
1733 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1735 /* Stop if there is no right child */
1736 if (!Vad
->RightChild
) break;
1738 /* Search on the right next */
1739 Vad
= Vad
->RightChild
;
1744 /* Was a VAD found? */
1747 Address
= PAGE_ALIGN(BaseAddress
);
1749 /* Calculate region size */
1752 if (Vad
->StartingVpn
>= BaseVpn
)
1754 /* Region size is the free space till the start of that VAD */
1755 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1759 /* Get the next VAD */
1760 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1763 /* Region size is the free space till the start of that VAD */
1764 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1768 /* Maximum possible region size with that base address */
1769 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1775 /* Maximum possible region size with that base address */
1776 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1779 /* Unlock the address space of the process */
1780 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1782 /* Check if we were attached */
1783 if (ProcessHandle
!= NtCurrentProcess())
1785 /* Detach and derefernece the process */
1786 KeUnstackDetachProcess(&ApcState
);
1787 ObDereferenceObject(TargetProcess
);
1790 /* Build the rest of the initial information block */
1791 MemoryInfo
.BaseAddress
= Address
;
1792 MemoryInfo
.AllocationBase
= NULL
;
1793 MemoryInfo
.AllocationProtect
= 0;
1794 MemoryInfo
.State
= MEM_FREE
;
1795 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1796 MemoryInfo
.Type
= 0;
1798 /* Return the data, NtQueryInformation already probed it*/
1799 if (PreviousMode
!= KernelMode
)
1803 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1804 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1806 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1808 Status
= _SEH2_GetExceptionCode();
1814 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1815 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1821 /* Set the correct memory type based on what kind of VAD this is */
1822 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1823 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1825 MemoryInfo
.Type
= MEM_PRIVATE
;
1827 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1829 MemoryInfo
.Type
= MEM_IMAGE
;
1833 MemoryInfo
.Type
= MEM_MAPPED
;
1836 /* Find the memory area the specified address belongs to */
1837 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1838 ASSERT(MemoryArea
!= NULL
);
1840 /* Determine information dependent on the memory area type */
1841 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1843 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1844 if (!NT_SUCCESS(Status
))
1846 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1847 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1848 NT_ASSERT(NT_SUCCESS(Status
));
1853 /* Build the initial information block */
1854 Address
= PAGE_ALIGN(BaseAddress
);
1855 MemoryInfo
.BaseAddress
= Address
;
1856 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1857 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1858 MemoryInfo
.Type
= MEM_PRIVATE
;
1860 /* Acquire the working set lock (shared is enough) */
1861 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1863 /* Find the largest chunk of memory which has the same state and protection mask */
1864 MemoryInfo
.State
= MiQueryAddressState(Address
,
1867 &MemoryInfo
.Protect
,
1869 Address
= NextAddress
;
1870 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1872 /* Keep going unless the state or protection mask changed */
1873 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1874 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1875 Address
= NextAddress
;
1878 /* Release the working set lock */
1879 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1881 /* Check if we went outside of the VAD */
1882 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1884 /* Set the end of the VAD as the end address */
1885 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1888 /* Now that we know the last VA address, calculate the region size */
1889 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1892 /* Unlock the address space of the process */
1893 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1895 /* Check if we were attached */
1896 if (ProcessHandle
!= NtCurrentProcess())
1898 /* Detach and derefernece the process */
1899 KeUnstackDetachProcess(&ApcState
);
1900 ObDereferenceObject(TargetProcess
);
1903 /* Return the data, NtQueryInformation already probed it */
1904 if (PreviousMode
!= KernelMode
)
1908 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1909 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1911 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1913 Status
= _SEH2_GetExceptionCode();
1919 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1920 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1924 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1925 "State: %lx Type: %lx Size: %lx\n",
1926 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1927 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1928 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1935 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1936 IN ULONG_PTR EndingAddress
,
1938 IN PEPROCESS Process
)
1940 PMMPTE PointerPte
, LastPte
, PointerPde
;
1941 BOOLEAN OnBoundary
= TRUE
;
1944 /* Get the PDE and PTE addresses */
1945 PointerPde
= MiAddressToPde(StartingAddress
);
1946 PointerPte
= MiAddressToPte(StartingAddress
);
1947 LastPte
= MiAddressToPte(EndingAddress
);
1949 /* Loop all the PTEs */
1950 while (PointerPte
<= LastPte
)
1952 /* Check if we've hit an new PDE boundary */
1955 /* Is this PDE demand zero? */
1956 PointerPde
= MiAddressToPte(PointerPte
);
1957 if (PointerPde
->u
.Long
!= 0)
1959 /* It isn't -- is it valid? */
1960 if (PointerPde
->u
.Hard
.Valid
== 0)
1962 /* Nope, fault it in */
1963 PointerPte
= MiPteToAddress(PointerPde
);
1964 MiMakeSystemAddressValid(PointerPte
, Process
);
1969 /* The PTE was already valid, so move to the next one */
1971 PointerPte
= MiPteToAddress(PointerPde
);
1973 /* Is the entire VAD committed? If not, fail */
1974 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1976 /* Everything is committed so far past the range, return true */
1977 if (PointerPte
> LastPte
) return TRUE
;
1981 /* Is the PTE demand zero? */
1982 if (PointerPte
->u
.Long
== 0)
1984 /* Is the entire VAD committed? If not, fail */
1985 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1989 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1990 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1991 (PointerPte
->u
.Hard
.Valid
== 0) &&
1992 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1993 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1995 /* Then part of the range is decommitted, so fail */
2000 /* Move to the next PTE */
2002 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2005 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2011 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2012 IN OUT PVOID
*BaseAddress
,
2013 IN OUT PSIZE_T NumberOfBytesToProtect
,
2014 IN ULONG NewAccessProtection
,
2015 OUT PULONG OldAccessProtection OPTIONAL
)
2017 PMEMORY_AREA MemoryArea
;
2018 PMMSUPPORT AddressSpace
;
2019 ULONG OldAccessProtection_
;
2022 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2023 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2025 AddressSpace
= &Process
->Vm
;
2026 MmLockAddressSpace(AddressSpace
);
2027 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2028 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2030 MmUnlockAddressSpace(AddressSpace
);
2031 return STATUS_UNSUCCESSFUL
;
2034 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2036 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2037 Status
= MmProtectSectionView(AddressSpace
,
2040 *NumberOfBytesToProtect
,
2041 NewAccessProtection
,
2042 OldAccessProtection
);
2044 MmUnlockAddressSpace(AddressSpace
);
2051 MiProtectVirtualMemory(IN PEPROCESS Process
,
2052 IN OUT PVOID
*BaseAddress
,
2053 IN OUT PSIZE_T NumberOfBytesToProtect
,
2054 IN ULONG NewAccessProtection
,
2055 OUT PULONG OldAccessProtection OPTIONAL
)
2057 PMEMORY_AREA MemoryArea
;
2059 PMMSUPPORT AddressSpace
;
2060 ULONG_PTR StartingAddress
, EndingAddress
;
2061 PMMPTE PointerPde
, PointerPte
, LastPte
;
2064 ULONG ProtectionMask
, OldProtect
;
2066 NTSTATUS Status
= STATUS_SUCCESS
;
2067 PETHREAD Thread
= PsGetCurrentThread();
2068 TABLE_SEARCH_RESULT Result
;
2070 /* Calculate base address for the VAD */
2071 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2072 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2074 /* Calculate the protection mask and make sure it's valid */
2075 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2076 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2078 DPRINT1("Invalid protection mask\n");
2079 return STATUS_INVALID_PAGE_PROTECTION
;
2082 /* Check for ROS specific memory area */
2083 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2084 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2087 return MiRosProtectVirtualMemory(Process
,
2089 NumberOfBytesToProtect
,
2090 NewAccessProtection
,
2091 OldAccessProtection
);
2094 /* Lock the address space and make sure the process isn't already dead */
2095 AddressSpace
= MmGetCurrentAddressSpace();
2096 MmLockAddressSpace(AddressSpace
);
2097 if (Process
->VmDeleted
)
2099 DPRINT1("Process is dying\n");
2100 Status
= STATUS_PROCESS_IS_TERMINATING
;
2104 /* Get the VAD for this address range, and make sure it exists */
2105 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2106 EndingAddress
>> PAGE_SHIFT
,
2108 (PMMADDRESS_NODE
*)&Vad
);
2109 if (Result
!= TableFoundNode
)
2111 DPRINT("Could not find a VAD for this allocation\n");
2112 Status
= STATUS_CONFLICTING_ADDRESSES
;
2116 /* Make sure the address is within this VAD's boundaries */
2117 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2118 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2120 Status
= STATUS_CONFLICTING_ADDRESSES
;
2124 /* These kinds of VADs are not supported atm */
2125 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2126 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2127 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2129 DPRINT1("Illegal VAD for attempting to set protection\n");
2130 Status
= STATUS_CONFLICTING_ADDRESSES
;
2134 /* Check for a VAD whose protection can't be changed */
2135 if (Vad
->u
.VadFlags
.NoChange
== 1)
2137 DPRINT1("Trying to change protection of a NoChange VAD\n");
2138 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2142 /* Is this section, or private memory? */
2143 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2145 /* Not yet supported */
2146 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2148 DPRINT1("Illegal VAD for attempting to set protection\n");
2149 Status
= STATUS_CONFLICTING_ADDRESSES
;
2153 /* Rotate VADs are not yet supported */
2154 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2156 DPRINT1("Illegal VAD for attempting to set protection\n");
2157 Status
= STATUS_CONFLICTING_ADDRESSES
;
2161 /* Not valid on section files */
2162 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2165 DPRINT1("Invalid protection flags for section\n");
2166 Status
= STATUS_INVALID_PARAMETER_4
;
2170 /* Check if data or page file mapping protection PTE is compatible */
2171 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2174 DPRINT1("Fixme: Not checking for valid protection\n");
2177 /* This is a section, and this is not yet supported */
2178 DPRINT1("Section protection not yet supported\n");
2183 /* Private memory, check protection flags */
2184 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2185 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2187 DPRINT1("Invalid protection flags for private memory\n");
2188 Status
= STATUS_INVALID_PARAMETER_4
;
2192 /* Lock the working set */
2193 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2195 /* Check if all pages in this range are committed */
2196 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2203 DPRINT1("The entire range is not committed\n");
2204 Status
= STATUS_NOT_COMMITTED
;
2205 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2209 /* Compute starting and ending PTE and PDE addresses */
2210 PointerPde
= MiAddressToPde(StartingAddress
);
2211 PointerPte
= MiAddressToPte(StartingAddress
);
2212 LastPte
= MiAddressToPte(EndingAddress
);
2214 /* Make this PDE valid */
2215 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2217 /* Save protection of the first page */
2218 if (PointerPte
->u
.Long
!= 0)
2220 /* Capture the page protection and make the PDE valid */
2221 OldProtect
= MiGetPageProtection(PointerPte
);
2222 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2226 /* Grab the old protection from the VAD itself */
2227 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2230 /* Loop all the PTEs now */
2231 while (PointerPte
<= LastPte
)
2233 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2234 if (MiIsPteOnPdeBoundary(PointerPte
))
2236 PointerPde
= MiAddressToPte(PointerPte
);
2237 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2240 /* Capture the PTE and check if it was empty */
2241 PteContents
= *PointerPte
;
2242 if (PteContents
.u
.Long
== 0)
2244 /* This used to be a zero PTE and it no longer is, so we must add a
2245 reference to the pagetable. */
2246 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2249 /* Check what kind of PTE we are dealing with */
2250 if (PteContents
.u
.Hard
.Valid
== 1)
2252 /* Get the PFN entry */
2253 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2255 /* We don't support this yet */
2256 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2258 /* Check if the page should not be accessible at all */
2259 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2260 (NewAccessProtection
& PAGE_GUARD
))
2262 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2264 /* Mark the PTE as transition and change its protection */
2265 PteContents
.u
.Hard
.Valid
= 0;
2266 PteContents
.u
.Soft
.Transition
= 1;
2267 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2268 /* Decrease PFN share count and write the PTE */
2269 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2270 // FIXME: remove the page from the WS
2271 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2273 // FIXME: Should invalidate entry in every CPU TLB
2276 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2278 /* We are done for this PTE */
2279 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2283 /* Write the protection mask and write it with a TLB flush */
2284 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2285 MiFlushTbAndCapture(Vad
,
2294 /* We don't support these cases yet */
2295 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2296 //ASSERT(PteContents.u.Soft.Transition == 0);
2298 /* The PTE is already demand-zero, just update the protection mask */
2299 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2300 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2301 ASSERT(PointerPte
->u
.Long
!= 0);
2304 /* Move to the next PTE */
2308 /* Unlock the working set */
2309 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2312 /* Unlock the address space */
2313 MmUnlockAddressSpace(AddressSpace
);
2315 /* Return parameters and success */
2316 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2317 *BaseAddress
= (PVOID
)StartingAddress
;
2318 *OldAccessProtection
= OldProtect
;
2319 return STATUS_SUCCESS
;
2322 /* Unlock the address space and return the failure code */
2323 MmUnlockAddressSpace(AddressSpace
);
2329 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2330 IN PEPROCESS TargetProcess
,
2333 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2336 // Sanity checks. The latter is because we only use this function with the
2337 // PFN lock not held, so it may go away in the future.
2339 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2340 ASSERT(OldIrql
== MM_NOIRQL
);
2343 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2344 // return the same address as the PDE on 2-level page table systems.
2346 // If everything is already valid, there is nothing to do.
2348 PointerPpe
= MiAddressToPte(PointerPde
);
2349 PointerPxe
= MiAddressToPde(PointerPde
);
2350 if ((PointerPxe
->u
.Hard
.Valid
) &&
2351 (PointerPpe
->u
.Hard
.Valid
) &&
2352 (PointerPde
->u
.Hard
.Valid
))
2358 // At least something is invalid, so begin by getting the PTE for the PDE itself
2359 // and then lookup each additional level. We must do it in this precise order
2360 // because the pagfault.c code (as well as in Windows) depends that the next
2361 // level up (higher) must be valid when faulting a lower level
2363 PointerPte
= MiPteToAddress(PointerPde
);
2367 // Make sure APCs continued to be disabled
2369 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2372 // First, make the PXE valid if needed
2374 if (!PointerPxe
->u
.Hard
.Valid
)
2376 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2377 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2383 if (!PointerPpe
->u
.Hard
.Valid
)
2385 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2386 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2390 // And finally, make the PDE itself valid.
2392 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2395 // This should've worked the first time so the loop is really just for
2396 // show -- ASSERT that we're actually NOT going to be looping.
2398 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2399 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2400 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2401 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2402 !(PointerPpe
->u
.Hard
.Valid
) ||
2403 !(PointerPde
->u
.Hard
.Valid
));
2408 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2414 PFN_NUMBER PageFrameIndex
;
2418 // Acquire the PFN lock and loop all the PTEs in the list
2420 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2421 for (i
= 0; i
!= Count
; i
++)
2424 // The PTE must currently be valid
2426 TempPte
= *ValidPteList
[i
];
2427 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2430 // Get the PFN entry for the page itself, and then for its page table
2432 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2433 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2434 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2437 // Decrement the share count on the page table, and then on the page
2440 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2441 MI_SET_PFN_DELETED(Pfn1
);
2442 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2445 // Make the page decommitted
2447 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2451 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2452 // and then release the PFN lock
2455 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2460 MiDecommitPages(IN PVOID StartingAddress
,
2461 IN PMMPTE EndingPte
,
2462 IN PEPROCESS Process
,
2465 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2466 ULONG CommitReduction
= 0;
2467 PMMPTE ValidPteList
[256];
2471 PETHREAD CurrentThread
= PsGetCurrentThread();
2474 // Get the PTE and PTE for the address, and lock the working set
2475 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2476 // commited range ends so that we can do the right accounting.
2478 PointerPde
= MiAddressToPde(StartingAddress
);
2479 PointerPte
= MiAddressToPte(StartingAddress
);
2480 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2481 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2484 // Make the PDE valid, and now loop through each page's worth of data
2486 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2487 while (PointerPte
<= EndingPte
)
2490 // Check if we've crossed a PDE boundary
2492 if (MiIsPteOnPdeBoundary(PointerPte
))
2495 // Get the new PDE and flush the valid PTEs we had built up until
2496 // now. This helps reduce the amount of TLB flushing we have to do.
2497 // Note that Windows does a much better job using timestamps and
2498 // such, and does not flush the entire TLB all the time, but right
2499 // now we have bigger problems to worry about than TLB flushing.
2501 PointerPde
= MiAddressToPde(StartingAddress
);
2504 MiProcessValidPteList(ValidPteList
, PteCount
);
2509 // Make this PDE valid
2511 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2515 // Read this PTE. It might be active or still demand-zero.
2517 PteContents
= *PointerPte
;
2518 if (PteContents
.u
.Long
)
2521 // The PTE is active. It might be valid and in a working set, or
2522 // it might be a prototype PTE or paged out or even in transition.
2524 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2527 // It's already decommited, so there's nothing for us to do here
2534 // Remove it from the counters, and check if it was valid or not
2536 //Process->NumberOfPrivatePages--;
2537 if (PteContents
.u
.Hard
.Valid
)
2540 // It's valid. At this point make sure that it is not a ROS
2541 // PFN. Also, we don't support ProtoPTEs in this code path.
2543 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2544 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2545 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2548 // Flush any pending PTEs that we had not yet flushed, if our
2549 // list has gotten too big, then add this PTE to the flush list.
2551 if (PteCount
== 256)
2553 MiProcessValidPteList(ValidPteList
, PteCount
);
2556 ValidPteList
[PteCount
++] = PointerPte
;
2561 // We do not support any of these other scenarios at the moment
2563 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2564 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2565 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2568 // So the only other possibility is that it is still a demand
2569 // zero PTE, in which case we undo the accounting we did
2570 // earlier and simply make the page decommitted.
2572 //Process->NumberOfPrivatePages++;
2573 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2580 // This used to be a zero PTE and it no longer is, so we must add a
2581 // reference to the pagetable.
2583 MiIncrementPageTableReferences(StartingAddress
);
2586 // Next, we account for decommitted PTEs and make the PTE as such
2588 if (PointerPte
> CommitPte
) CommitReduction
++;
2589 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2593 // Move to the next PTE and the next address
2596 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2600 // Flush any dangling PTEs from the loop in the last page table, and then
2601 // release the working set and return the commit reduction accounting.
2603 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2604 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2605 return CommitReduction
;
2608 /* PUBLIC FUNCTIONS ***********************************************************/
2615 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2626 MmSecureVirtualMemory(IN PVOID Address
,
2630 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2639 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2641 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2644 /* SYSTEM CALLS ***************************************************************/
2648 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2649 IN PVOID BaseAddress
,
2651 IN SIZE_T NumberOfBytesToRead
,
2652 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2654 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2656 NTSTATUS Status
= STATUS_SUCCESS
;
2657 SIZE_T BytesRead
= 0;
2661 // Check if we came from user mode
2663 if (PreviousMode
!= KernelMode
)
2666 // Validate the read addresses
2668 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2669 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2670 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2671 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2674 // Don't allow to write into kernel space
2676 return STATUS_ACCESS_VIOLATION
;
2680 // Enter SEH for probe
2685 // Probe the output value
2687 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2692 // Get exception code
2694 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2700 // Don't do zero-byte transfers
2702 if (NumberOfBytesToRead
)
2705 // Reference the process
2707 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2713 if (NT_SUCCESS(Status
))
2718 Status
= MmCopyVirtualMemory(Process
,
2720 PsGetCurrentProcess(),
2722 NumberOfBytesToRead
,
2727 // Dereference the process
2729 ObDereferenceObject(Process
);
2734 // Check if the caller sent this parameter
2736 if (NumberOfBytesRead
)
2739 // Enter SEH to guard write
2744 // Return the number of bytes read
2746 *NumberOfBytesRead
= BytesRead
;
2748 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2762 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2763 IN PVOID BaseAddress
,
2765 IN SIZE_T NumberOfBytesToWrite
,
2766 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2768 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2770 NTSTATUS Status
= STATUS_SUCCESS
;
2771 SIZE_T BytesWritten
= 0;
2775 // Check if we came from user mode
2777 if (PreviousMode
!= KernelMode
)
2780 // Validate the read addresses
2782 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2783 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2784 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2785 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2788 // Don't allow to write into kernel space
2790 return STATUS_ACCESS_VIOLATION
;
2794 // Enter SEH for probe
2799 // Probe the output value
2801 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2803 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2806 // Get exception code
2808 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2814 // Don't do zero-byte transfers
2816 if (NumberOfBytesToWrite
)
2819 // Reference the process
2821 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2827 if (NT_SUCCESS(Status
))
2832 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2836 NumberOfBytesToWrite
,
2841 // Dereference the process
2843 ObDereferenceObject(Process
);
2848 // Check if the caller sent this parameter
2850 if (NumberOfBytesWritten
)
2853 // Enter SEH to guard write
2858 // Return the number of bytes written
2860 *NumberOfBytesWritten
= BytesWritten
;
2862 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2876 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2877 IN OUT PVOID
*UnsafeBaseAddress
,
2878 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2879 IN ULONG NewAccessProtection
,
2880 OUT PULONG UnsafeOldAccessProtection
)
2883 ULONG OldAccessProtection
;
2885 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2886 PVOID BaseAddress
= NULL
;
2887 SIZE_T NumberOfBytesToProtect
= 0;
2888 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2890 BOOLEAN Attached
= FALSE
;
2891 KAPC_STATE ApcState
;
2895 // Check for valid protection flags
2897 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2898 if (Protection
!= PAGE_NOACCESS
&&
2899 Protection
!= PAGE_READONLY
&&
2900 Protection
!= PAGE_READWRITE
&&
2901 Protection
!= PAGE_WRITECOPY
&&
2902 Protection
!= PAGE_EXECUTE
&&
2903 Protection
!= PAGE_EXECUTE_READ
&&
2904 Protection
!= PAGE_EXECUTE_READWRITE
&&
2905 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2910 return STATUS_INVALID_PAGE_PROTECTION
;
2914 // Check if we came from user mode
2916 if (PreviousMode
!= KernelMode
)
2919 // Enter SEH for probing
2924 // Validate all outputs
2926 ProbeForWritePointer(UnsafeBaseAddress
);
2927 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2928 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2933 BaseAddress
= *UnsafeBaseAddress
;
2934 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2936 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2939 // Get exception code
2941 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2950 BaseAddress
= *UnsafeBaseAddress
;
2951 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2955 // Catch illegal base address
2957 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2960 // Catch illegal region size
2962 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2967 return STATUS_INVALID_PARAMETER_3
;
2971 // 0 is also illegal
2973 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2976 // Get a reference to the process
2978 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2979 PROCESS_VM_OPERATION
,
2984 if (!NT_SUCCESS(Status
)) return Status
;
2987 // Check if we should attach
2989 if (CurrentProcess
!= Process
)
2994 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2999 // Do the actual work
3001 Status
= MiProtectVirtualMemory(Process
,
3003 &NumberOfBytesToProtect
,
3004 NewAccessProtection
,
3005 &OldAccessProtection
);
3010 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3013 // Release reference
3015 ObDereferenceObject(Process
);
3018 // Enter SEH to return data
3023 // Return data to user
3025 *UnsafeOldAccessProtection
= OldAccessProtection
;
3026 *UnsafeBaseAddress
= BaseAddress
;
3027 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3029 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3046 // HACK until we have proper WSLIST support
3047 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3049 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3051 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3063 // HACK until we have proper WSLIST support
3064 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3066 if (!Wsle
->u1
.e1
.LockedInWs
&&
3067 !Wsle
->u1
.e1
.LockedInMemory
)
3069 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3072 if (LockType
& MAP_PROCESS
)
3073 Wsle
->u1
.e1
.LockedInWs
= 1;
3074 if (LockType
& MAP_SYSTEM
)
3075 Wsle
->u1
.e1
.LockedInMemory
= 1;
3084 // HACK until we have proper WSLIST support
3085 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3087 if (LockType
& MAP_PROCESS
)
3088 Wsle
->u1
.e1
.LockedInWs
= 0;
3089 if (LockType
& MAP_SYSTEM
)
3090 Wsle
->u1
.e1
.LockedInMemory
= 0;
3092 if (!Wsle
->u1
.e1
.LockedInWs
&&
3093 !Wsle
->u1
.e1
.LockedInMemory
)
3095 MiDereferencePfnAndDropLockCount(Pfn1
);
3101 MiCheckVadsForLockOperation(
3102 _Inout_ PVOID
*BaseAddress
,
3103 _Inout_ PSIZE_T RegionSize
,
3104 _Inout_ PVOID
*EndAddress
)
3110 /* Get the base address and align the start address */
3111 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3112 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3113 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3115 /* First loop and check all VADs */
3116 CurrentVa
= *BaseAddress
;
3117 while (CurrentVa
< *EndAddress
)
3120 Vad
= MiLocateAddress(CurrentVa
);
3123 /// FIXME: this might be a memory area for a section view...
3124 return STATUS_ACCESS_VIOLATION
;
3127 /* Check VAD type */
3128 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3129 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3130 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3132 *EndAddress
= CurrentVa
;
3133 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3134 return STATUS_INCOMPATIBLE_FILE_MAP
;
3137 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3140 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3141 return STATUS_SUCCESS
;
3146 MiLockVirtualMemory(
3147 IN OUT PVOID
*BaseAddress
,
3148 IN OUT PSIZE_T RegionSize
,
3151 PEPROCESS CurrentProcess
;
3152 PMMSUPPORT AddressSpace
;
3153 PVOID CurrentVa
, EndAddress
;
3154 PMMPTE PointerPte
, LastPte
;
3156 #if (_MI_PAGING_LEVELS >= 3)
3159 #if (_MI_PAGING_LEVELS == 4)
3163 NTSTATUS Status
, TempStatus
;
3165 /* Lock the address space */
3166 AddressSpace
= MmGetCurrentAddressSpace();
3167 MmLockAddressSpace(AddressSpace
);
3169 /* Make sure we still have an address space */
3170 CurrentProcess
= PsGetCurrentProcess();
3171 if (CurrentProcess
->VmDeleted
)
3173 Status
= STATUS_PROCESS_IS_TERMINATING
;
3177 /* Check the VADs in the requested range */
3178 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3179 if (!NT_SUCCESS(Status
))
3184 /* Enter SEH for probing */
3187 /* Loop all pages and probe them */
3188 CurrentVa
= *BaseAddress
;
3189 while (CurrentVa
< EndAddress
)
3191 (void)(*(volatile CHAR
*)CurrentVa
);
3192 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3195 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3197 Status
= _SEH2_GetExceptionCode();
3202 /* All pages were accessible, since we hold the address space lock, nothing
3203 can be de-committed. Assume success for now. */
3204 Status
= STATUS_SUCCESS
;
3206 /* Get the PTE and PDE */
3207 PointerPte
= MiAddressToPte(*BaseAddress
);
3208 PointerPde
= MiAddressToPde(*BaseAddress
);
3209 #if (_MI_PAGING_LEVELS >= 3)
3210 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3212 #if (_MI_PAGING_LEVELS == 4)
3213 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3216 /* Get the last PTE */
3217 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3219 /* Lock the process working set */
3220 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3222 /* Loop the pages */
3225 /* Check for a page that is not accessible */
3227 #if (_MI_PAGING_LEVELS == 4)
3228 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3230 #if (_MI_PAGING_LEVELS >= 3)
3231 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3233 (PointerPde
->u
.Hard
.Valid
== 0) ||
3234 (PointerPte
->u
.Hard
.Valid
== 0))
3236 /* Release process working set */
3237 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3239 /* Access the page */
3240 CurrentVa
= MiPteToAddress(PointerPte
);
3242 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3243 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3244 if (!NT_SUCCESS(TempStatus
))
3246 // This should only happen, when remote backing storage is not accessible
3248 Status
= TempStatus
;
3252 /* Lock the process working set */
3253 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3257 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3258 ASSERT(Pfn1
!= NULL
);
3260 /* Check the previous lock status */
3261 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3263 Status
= STATUS_WAS_LOCKED
;
3267 MI_LOCK_VA(Pfn1
, MapType
);
3269 /* Go to the next PTE */
3272 /* Check if we're on a PDE boundary */
3273 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3274 #if (_MI_PAGING_LEVELS >= 3)
3275 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3277 #if (_MI_PAGING_LEVELS == 4)
3278 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3280 } while (PointerPte
<= LastPte
);
3282 /* Release process working set */
3283 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3286 /* Unlock address space */
3287 MmUnlockAddressSpace(AddressSpace
);
3294 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3295 IN OUT PVOID
*BaseAddress
,
3296 IN OUT PSIZE_T NumberOfBytesToLock
,
3300 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3302 BOOLEAN Attached
= FALSE
;
3303 KAPC_STATE ApcState
;
3304 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3305 PVOID CapturedBaseAddress
;
3306 SIZE_T CapturedBytesToLock
;
3312 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3315 // Invalid set of flags
3317 return STATUS_INVALID_PARAMETER
;
3321 // At least one flag must be specified
3323 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3328 return STATUS_INVALID_PARAMETER
;
3332 // Enter SEH for probing
3337 // Validate output data
3339 ProbeForWritePointer(BaseAddress
);
3340 ProbeForWriteSize_t(NumberOfBytesToLock
);
3345 CapturedBaseAddress
= *BaseAddress
;
3346 CapturedBytesToLock
= *NumberOfBytesToLock
;
3348 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3351 // Get exception code
3353 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3358 // Catch illegal base address
3360 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3363 // Catch illegal region size
3365 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3370 return STATUS_INVALID_PARAMETER
;
3374 // 0 is also illegal
3376 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3379 // Get a reference to the process
3381 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3382 PROCESS_VM_OPERATION
,
3387 if (!NT_SUCCESS(Status
)) return Status
;
3390 // Check if this is is system-mapped
3392 if (MapType
& MAP_SYSTEM
)
3395 // Check for required privilege
3397 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3400 // Fail: Don't have it
3402 ObDereferenceObject(Process
);
3403 return STATUS_PRIVILEGE_NOT_HELD
;
3408 // Check if we should attach
3410 if (CurrentProcess
!= Process
)
3415 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);