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 and paged out PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 ASSERT(TempPte
.u
.Soft
.PageFileHigh
== 0);
414 if (TempPte
.u
.Soft
.Transition
)
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 DPRINT("Pte %p is transitional!\n", PointerPte
);
422 /* Destroy the PTE */
423 MI_ERASE_PTE(PointerPte
);
425 /* Drop the reference on the page table. */
426 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
428 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
430 /* Make the page free. For prototypes, it will be made free when deleting the section object */
431 if (Pfn1
->u2
.ShareCount
== 0)
433 NT_ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
435 /* And it should be in standby or modified list */
436 ASSERT((Pfn1
->u3
.e1
.PageLocation
== ModifiedPageList
) || (Pfn1
->u3
.e1
.PageLocation
== StandbyPageList
));
438 /* Unlink it and temporarily mark it as active */
439 MiUnlinkPageFromList(Pfn1
);
440 Pfn1
->u3
.e2
.ReferenceCount
++;
441 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
443 /* This will put it back in free list and clean properly up */
444 MI_SET_PFN_DELETED(Pfn1
);
445 MiDecrementReferenceCount(Pfn1
, PageFrameIndex
);
451 /* Get the PFN entry */
452 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
453 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
455 /* Check if this is a valid, prototype PTE */
456 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
458 /* Get the PDE and make sure it's faulted in */
459 PointerPde
= MiPteToPde(PointerPte
);
460 if (PointerPde
->u
.Hard
.Valid
== 0)
462 #if (_MI_PAGING_LEVELS == 2)
463 /* Could be paged pool access from a new process -- synchronize the page directories */
464 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
467 /* The PDE must be valid at this point */
468 KeBugCheckEx(MEMORY_MANAGEMENT
,
470 (ULONG_PTR
)PointerPte
,
472 (ULONG_PTR
)VirtualAddress
);
474 #if (_MI_PAGING_LEVELS == 2)
477 /* Drop the share count on the page table */
478 PointerPde
= MiPteToPde(PointerPte
);
479 MiDecrementShareCount(MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
),
480 PointerPde
->u
.Hard
.PageFrameNumber
);
482 /* Drop the share count */
483 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
485 /* Either a fork, or this is the shared user data page */
486 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
488 /* If it's not the shared user page, then crash, since there's no fork() yet */
489 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
490 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
492 /* Must be some sort of memory corruption */
493 KeBugCheckEx(MEMORY_MANAGEMENT
,
495 (ULONG_PTR
)PointerPte
,
496 (ULONG_PTR
)PrototypePte
,
497 (ULONG_PTR
)Pfn1
->PteAddress
);
502 MI_ERASE_PTE(PointerPte
);
506 /* Make sure the saved PTE address is valid */
507 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
509 /* The PFN entry is illegal, or invalid */
510 KeBugCheckEx(MEMORY_MANAGEMENT
,
512 (ULONG_PTR
)PointerPte
,
514 (ULONG_PTR
)Pfn1
->PteAddress
);
518 MI_ERASE_PTE(PointerPte
);
520 /* There should only be 1 shared reference count */
521 ASSERT(Pfn1
->u2
.ShareCount
== 1);
523 /* Drop the reference on the page table. */
524 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
526 /* Mark the PFN for deletion and dereference what should be the last ref */
527 MI_SET_PFN_DELETED(Pfn1
);
528 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
530 /* We should eventually do this */
531 //CurrentProcess->NumberOfPrivatePages--;
540 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
541 IN ULONG_PTR EndingAddress
,
544 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
547 PEPROCESS CurrentProcess
;
549 BOOLEAN AddressGap
= FALSE
;
550 PSUBSECTION Subsection
;
552 /* Get out if this is a fake VAD, RosMm will free the marea pages */
553 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
555 /* Grab the process and PTE/PDE for the address being deleted */
556 CurrentProcess
= PsGetCurrentProcess();
557 PointerPde
= MiAddressToPde(Va
);
558 PointerPte
= MiAddressToPte(Va
);
560 /* Check if this is a section VAD or a VM VAD */
561 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
563 /* Don't worry about prototypes */
564 PrototypePte
= LastPrototypePte
= NULL
;
568 /* Get the prototype PTE */
569 PrototypePte
= Vad
->FirstPrototypePte
;
570 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
573 /* In all cases, we don't support fork() yet */
574 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
576 /* Loop the PTE for each VA */
579 /* First keep going until we find a valid PDE */
580 while (!PointerPde
->u
.Long
)
582 /* There are gaps in the address space */
585 /* Still no valid PDE, try the next 4MB (or whatever) */
588 /* Update the PTE on this new boundary */
589 PointerPte
= MiPteToAddress(PointerPde
);
591 /* Check if all the PDEs are invalid, so there's nothing to free */
592 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
593 if (Va
> EndingAddress
) return;
596 /* Now check if the PDE is mapped in */
597 if (!PointerPde
->u
.Hard
.Valid
)
599 /* It isn't, so map it in */
600 PointerPte
= MiPteToAddress(PointerPde
);
601 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
604 /* Now we should have a valid PDE, mapped in, and still have some VA */
605 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
606 ASSERT(Va
<= EndingAddress
);
608 /* Check if this is a section VAD with gaps in it */
609 if ((AddressGap
) && (LastPrototypePte
))
611 /* We need to skip to the next correct prototype PTE */
612 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
614 /* And we need the subsection to skip to the next last prototype PTE */
615 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
619 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
623 /* No more subsections, we are done with prototype PTEs */
628 /* Lock the PFN Database while we delete the PTEs */
629 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
632 /* Capture the PDE and make sure it exists */
633 TempPte
= *PointerPte
;
636 MiDecrementPageTableReferences((PVOID
)Va
);
638 /* Check if the PTE is actually mapped in */
639 if (MI_IS_MAPPED_PTE(&TempPte
))
641 /* Are we dealing with section VAD? */
642 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
644 /* We need to skip to the next correct prototype PTE */
645 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
647 /* And we need the subsection to skip to the next last prototype PTE */
648 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
652 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
656 /* No more subsections, we are done with prototype PTEs */
661 /* Check for prototype PTE */
662 if ((TempPte
.u
.Hard
.Valid
== 0) &&
663 (TempPte
.u
.Soft
.Prototype
== 1))
666 MI_ERASE_PTE(PointerPte
);
670 /* Delete the PTE proper */
671 MiDeletePte(PointerPte
,
679 /* The PTE was never mapped, just nuke it here */
680 MI_ERASE_PTE(PointerPte
);
684 /* Update the address and PTE for it */
689 /* Making sure the PDE is still valid */
690 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
692 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
694 /* The PDE should still be valid at this point */
695 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
697 /* Check remaining PTE count (go back 1 page due to above loop) */
698 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
700 if (PointerPde
->u
.Long
!= 0)
702 /* Delete the PTE proper */
703 MiDeletePte(PointerPde
,
704 MiPteToAddress(PointerPde
),
710 /* Release the lock and get out if we're done */
711 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
712 if (Va
> EndingAddress
) return;
714 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
715 PointerPde
= MiAddressToPde(Va
);
721 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
722 OUT PBOOLEAN HaveBadAddress
,
723 OUT PULONG_PTR BadAddress
)
725 PEXCEPTION_RECORD ExceptionRecord
;
731 *HaveBadAddress
= FALSE
;
734 // Get the exception record
736 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
739 // Look at the exception code
741 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
742 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
743 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
746 // We can tell the address if we have more than one parameter
748 if (ExceptionRecord
->NumberParameters
> 1)
751 // Return the address
753 *HaveBadAddress
= TRUE
;
754 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
759 // Continue executing the next handler
761 return EXCEPTION_EXECUTE_HANDLER
;
766 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
767 IN PVOID SourceAddress
,
768 IN PEPROCESS TargetProcess
,
769 OUT PVOID TargetAddress
,
770 IN SIZE_T BufferSize
,
771 IN KPROCESSOR_MODE PreviousMode
,
772 OUT PSIZE_T ReturnSize
)
774 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
775 PMDL Mdl
= (PMDL
)MdlBuffer
;
776 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
777 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
778 volatile BOOLEAN PagesLocked
;
779 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
780 volatile PVOID MdlAddress
;
782 BOOLEAN HaveBadAddress
;
783 ULONG_PTR BadAddress
;
784 NTSTATUS Status
= STATUS_SUCCESS
;
788 // Calculate the maximum amount of data to move
790 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
791 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
792 CurrentSize
= TotalSize
;
793 RemainingSize
= BufferSize
;
796 // Loop as long as there is still data
798 while (RemainingSize
> 0)
801 // Check if this transfer will finish everything off
803 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
806 // Attach to the source address space
808 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
811 // Reset state for this pass
815 FailedInMoving
= FALSE
;
816 ASSERT(FailedInProbe
== FALSE
);
819 // Protect user-mode copy
824 // If this is our first time, probe the buffer
826 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
829 // Catch a failure here
831 FailedInProbe
= TRUE
;
836 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
841 FailedInProbe
= FALSE
;
845 // Initialize and probe and lock the MDL
847 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
848 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
854 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
863 // Use our SEH handler to pick this up
865 FailedInMapping
= TRUE
;
866 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
870 // Now let go of the source and grab to the target process
872 KeUnstackDetachProcess(&ApcState
);
873 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
876 // Check if this is our first time through
878 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
881 // Catch a failure here
883 FailedInProbe
= TRUE
;
888 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
893 FailedInProbe
= FALSE
;
897 // Now do the actual move
899 FailedInMoving
= TRUE
;
900 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
902 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
907 // Detach from whoever we may be attached to
909 KeUnstackDetachProcess(&ApcState
);
912 // Check if we had mapped the pages
914 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
917 // Check if we had locked the pages
919 if (PagesLocked
) MmUnlockPages(Mdl
);
922 // Check if we hit working set quota
924 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
929 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
933 // Check if we failed during the probe or mapping
935 if ((FailedInProbe
) || (FailedInMapping
))
940 Status
= _SEH2_GetExceptionCode();
941 _SEH2_YIELD(return Status
);
945 // Otherwise, we failed probably during the move
947 *ReturnSize
= BufferSize
- RemainingSize
;
951 // Check if we know exactly where we stopped copying
956 // Return the exact number of bytes copied
958 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
963 // Return partial copy
965 Status
= STATUS_PARTIAL_COPY
;
970 // Check for SEH status
972 if (Status
!= STATUS_SUCCESS
) return Status
;
975 // Detach from target
977 KeUnstackDetachProcess(&ApcState
);
982 MmUnmapLockedPages(MdlAddress
, Mdl
);
986 // Update location and size
988 RemainingSize
-= CurrentSize
;
989 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
990 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
996 *ReturnSize
= BufferSize
;
997 return STATUS_SUCCESS
;
1002 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
1003 IN PVOID SourceAddress
,
1004 IN PEPROCESS TargetProcess
,
1005 OUT PVOID TargetAddress
,
1006 IN SIZE_T BufferSize
,
1007 IN KPROCESSOR_MODE PreviousMode
,
1008 OUT PSIZE_T ReturnSize
)
1010 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
1011 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
1012 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
1013 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
1015 KAPC_STATE ApcState
;
1016 BOOLEAN HaveBadAddress
;
1017 ULONG_PTR BadAddress
;
1018 NTSTATUS Status
= STATUS_SUCCESS
;
1022 // Calculate the maximum amount of data to move
1024 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1025 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1026 CurrentSize
= TotalSize
;
1027 RemainingSize
= BufferSize
;
1030 // Check if we can use the stack
1032 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1037 PoolAddress
= (PVOID
)StackBuffer
;
1044 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1045 if (!PoolAddress
) ASSERT(FALSE
);
1046 HavePoolAddress
= TRUE
;
1050 // Loop as long as there is still data
1052 while (RemainingSize
> 0)
1055 // Check if this transfer will finish everything off
1057 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1060 // Attach to the source address space
1062 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1065 // Reset state for this pass
1067 FailedInMoving
= FALSE
;
1068 ASSERT(FailedInProbe
== FALSE
);
1071 // Protect user-mode copy
1076 // If this is our first time, probe the buffer
1078 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1081 // Catch a failure here
1083 FailedInProbe
= TRUE
;
1088 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1093 FailedInProbe
= FALSE
;
1099 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1102 // Now let go of the source and grab to the target process
1104 KeUnstackDetachProcess(&ApcState
);
1105 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1108 // Check if this is our first time through
1110 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1113 // Catch a failure here
1115 FailedInProbe
= TRUE
;
1120 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1125 FailedInProbe
= FALSE
;
1129 // Now do the actual move
1131 FailedInMoving
= TRUE
;
1132 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1134 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1139 // Detach from whoever we may be attached to
1141 KeUnstackDetachProcess(&ApcState
);
1144 // Check if we had allocated pool
1146 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1149 // Check if we failed during the probe
1156 Status
= _SEH2_GetExceptionCode();
1157 _SEH2_YIELD(return Status
);
1161 // Otherwise, we failed, probably during the move
1163 *ReturnSize
= BufferSize
- RemainingSize
;
1167 // Check if we know exactly where we stopped copying
1172 // Return the exact number of bytes copied
1174 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1179 // Return partial copy
1181 Status
= STATUS_PARTIAL_COPY
;
1186 // Check for SEH status
1188 if (Status
!= STATUS_SUCCESS
) return Status
;
1191 // Detach from target
1193 KeUnstackDetachProcess(&ApcState
);
1196 // Update location and size
1198 RemainingSize
-= CurrentSize
;
1199 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1200 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1205 // Check if we had allocated pool
1207 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1212 *ReturnSize
= BufferSize
;
1213 return STATUS_SUCCESS
;
1218 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1219 IN PVOID SourceAddress
,
1220 IN PEPROCESS TargetProcess
,
1221 OUT PVOID TargetAddress
,
1222 IN SIZE_T BufferSize
,
1223 IN KPROCESSOR_MODE PreviousMode
,
1224 OUT PSIZE_T ReturnSize
)
1227 PEPROCESS Process
= SourceProcess
;
1230 // Don't accept zero-sized buffers
1232 if (!BufferSize
) return STATUS_SUCCESS
;
1235 // If we are copying from ourselves, lock the target instead
1237 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1240 // Acquire rundown protection
1242 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1247 return STATUS_PROCESS_IS_TERMINATING
;
1251 // See if we should use the pool copy
1253 if (BufferSize
> MI_POOL_COPY_BYTES
)
1258 Status
= MiDoMappedCopy(SourceProcess
,
1271 Status
= MiDoPoolCopy(SourceProcess
,
1283 ExReleaseRundownProtection(&Process
->RundownProtect
);
1289 MmFlushVirtualMemory(IN PEPROCESS Process
,
1290 IN OUT PVOID
*BaseAddress
,
1291 IN OUT PSIZE_T RegionSize
,
1292 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1300 return STATUS_SUCCESS
;
1305 MiGetPageProtection(IN PMMPTE PointerPte
)
1309 PEPROCESS CurrentProcess
;
1310 PETHREAD CurrentThread
;
1311 BOOLEAN WsSafe
, WsShared
;
1316 /* Copy this PTE's contents */
1317 TempPte
= *PointerPte
;
1319 /* Assure it's not totally zero */
1320 ASSERT(TempPte
.u
.Long
);
1322 /* Check for a special prototype format */
1323 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1324 (TempPte
.u
.Soft
.Prototype
== 1))
1326 /* Check if the prototype PTE is not yet pointing to a PTE */
1327 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1329 /* The prototype PTE contains the protection */
1330 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1333 /* Get a pointer to the underlying shared PTE */
1334 PointerPte
= MiProtoPteToPte(&TempPte
);
1336 /* Since the PTE we want to read can be paged out at any time, we need
1337 to release the working set lock first, so that it can be paged in */
1338 CurrentThread
= PsGetCurrentThread();
1339 CurrentProcess
= PsGetCurrentProcess();
1340 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1345 /* Now read the PTE value */
1346 TempPte
= *PointerPte
;
1348 /* Check if that one is invalid */
1349 if (!TempPte
.u
.Hard
.Valid
)
1351 /* We get the protection directly from this PTE */
1352 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1356 /* The PTE is valid, so we might need to get the protection from
1357 the PFN. Lock the PFN database */
1358 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1360 /* Check if the PDE is still valid */
1361 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1363 /* It's not, make it valid */
1364 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1367 /* Now it's safe to read the PTE value again */
1368 TempPte
= *PointerPte
;
1369 ASSERT(TempPte
.u
.Long
!= 0);
1371 /* Check again if the PTE is invalid */
1372 if (!TempPte
.u
.Hard
.Valid
)
1374 /* The PTE is not valid, so we can use it's protection field */
1375 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1379 /* The PTE is valid, so we can find the protection in the
1380 OriginalPte field of the PFN */
1381 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1382 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1385 /* Release the PFN database */
1386 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1389 /* Lock the working set again */
1390 MiLockProcessWorkingSetForFault(CurrentProcess
,
1398 /* In the easy case of transition or demand zero PTE just return its protection */
1399 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1401 /* If we get here, the PTE is valid, so look up the page in PFN database */
1402 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1403 if (!Pfn
->u3
.e1
.PrototypePte
)
1405 /* Return protection of the original pte */
1406 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1407 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1410 /* This is software PTE */
1411 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1412 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1413 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1414 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1415 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1420 MiQueryAddressState(IN PVOID Va
,
1422 IN PEPROCESS TargetProcess
,
1423 OUT PULONG ReturnedProtect
,
1427 PMMPTE PointerPte
, ProtoPte
;
1429 #if (_MI_PAGING_LEVELS >= 3)
1432 #if (_MI_PAGING_LEVELS >= 4)
1435 MMPTE TempPte
, TempProtoPte
;
1436 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1437 ULONG State
= MEM_RESERVE
, Protect
= 0;
1438 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1439 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1441 /* Only normal VADs supported */
1442 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1444 /* Get the PDE and PTE for the address */
1445 PointerPde
= MiAddressToPde(Va
);
1446 PointerPte
= MiAddressToPte(Va
);
1447 #if (_MI_PAGING_LEVELS >= 3)
1448 PointerPpe
= MiAddressToPpe(Va
);
1450 #if (_MI_PAGING_LEVELS >= 4)
1451 PointerPxe
= MiAddressToPxe(Va
);
1454 /* Return the next range */
1455 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1459 #if (_MI_PAGING_LEVELS >= 4)
1460 /* Does the PXE exist? */
1461 if (PointerPxe
->u
.Long
== 0)
1463 /* It does not, next range starts at the next PXE */
1464 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1468 /* Is the PXE valid? */
1469 if (PointerPxe
->u
.Hard
.Valid
== 0)
1471 /* Is isn't, fault it in (make the PPE accessible) */
1472 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1475 #if (_MI_PAGING_LEVELS >= 3)
1476 /* Does the PPE exist? */
1477 if (PointerPpe
->u
.Long
== 0)
1479 /* It does not, next range starts at the next PPE */
1480 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1484 /* Is the PPE valid? */
1485 if (PointerPpe
->u
.Hard
.Valid
== 0)
1487 /* Is isn't, fault it in (make the PDE accessible) */
1488 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1492 /* Does the PDE exist? */
1493 if (PointerPde
->u
.Long
== 0)
1495 /* It does not, next range starts at the next PDE */
1496 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1500 /* Is the PDE valid? */
1501 if (PointerPde
->u
.Hard
.Valid
== 0)
1503 /* Is isn't, fault it in (make the PTE accessible) */
1504 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1507 /* We have a PTE that we can access now! */
1512 /* Is it safe to try reading the PTE? */
1515 /* FIXME: watch out for large pages */
1516 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1518 /* Capture the PTE */
1519 TempPte
= *PointerPte
;
1520 if (TempPte
.u
.Long
!= 0)
1522 /* The PTE is valid, so it's not zeroed out */
1523 DemandZeroPte
= FALSE
;
1525 /* Is it a decommited, invalid, or faulted PTE? */
1526 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1527 (TempPte
.u
.Hard
.Valid
== 0) &&
1528 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1529 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1531 /* Otherwise our defaults should hold */
1532 ASSERT(Protect
== 0);
1533 ASSERT(State
== MEM_RESERVE
);
1537 /* This means it's committed */
1540 /* We don't support these */
1541 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1542 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1543 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1545 /* Get protection state of this page */
1546 Protect
= MiGetPageProtection(PointerPte
);
1548 /* Check if this is an image-backed VAD */
1549 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1550 (TempPte
.u
.Soft
.Prototype
== 1) &&
1551 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1554 DPRINT1("Not supported\n");
1561 /* Check if this was a demand-zero PTE, since we need to find the state */
1564 /* Not yet handled */
1565 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1566 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1568 /* Check if this is private commited memory, or an section-backed VAD */
1569 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1571 /* Tell caller about the next range */
1572 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1574 /* Get the prototype PTE for this VAD */
1575 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1576 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1579 /* We should unlock the working set, but it's not being held! */
1581 /* Is the prototype PTE actually valid (committed)? */
1582 TempProtoPte
= *ProtoPte
;
1583 if (TempProtoPte
.u
.Long
)
1585 /* Unless this is a memory-mapped file, handle it like private VAD */
1587 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1588 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1591 /* We should re-lock the working set */
1594 else if (Vad
->u
.VadFlags
.MemCommit
)
1596 /* This is committed memory */
1599 /* Convert the protection */
1600 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1604 /* Return the protection code */
1605 *ReturnedProtect
= Protect
;
1611 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1612 IN PVOID BaseAddress
,
1613 OUT PVOID MemoryInformation
,
1614 IN SIZE_T MemoryInformationLength
,
1615 OUT PSIZE_T ReturnLength
)
1617 PEPROCESS TargetProcess
;
1618 NTSTATUS Status
= STATUS_SUCCESS
;
1620 PVOID Address
, NextAddress
;
1621 BOOLEAN Found
= FALSE
;
1622 ULONG NewProtect
, NewState
;
1624 MEMORY_BASIC_INFORMATION MemoryInfo
;
1625 KAPC_STATE ApcState
;
1626 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1627 PMEMORY_AREA MemoryArea
;
1628 SIZE_T ResultLength
;
1630 /* Check for illegal addresses in user-space, or the shared memory area */
1631 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1632 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1634 Address
= PAGE_ALIGN(BaseAddress
);
1636 /* Make up an info structure describing this range */
1637 MemoryInfo
.BaseAddress
= Address
;
1638 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1639 MemoryInfo
.Type
= MEM_PRIVATE
;
1641 /* Special case for shared data */
1642 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1644 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1645 MemoryInfo
.State
= MEM_COMMIT
;
1646 MemoryInfo
.Protect
= PAGE_READONLY
;
1647 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1651 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1652 MemoryInfo
.State
= MEM_RESERVE
;
1653 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1654 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1657 /* Return the data, NtQueryInformation already probed it*/
1658 if (PreviousMode
!= KernelMode
)
1662 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1663 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1665 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1667 Status
= _SEH2_GetExceptionCode();
1673 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1674 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1680 /* Check if this is for a local or remote process */
1681 if (ProcessHandle
== NtCurrentProcess())
1683 TargetProcess
= PsGetCurrentProcess();
1687 /* Reference the target process */
1688 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1689 PROCESS_QUERY_INFORMATION
,
1691 ExGetPreviousMode(),
1692 (PVOID
*)&TargetProcess
,
1694 if (!NT_SUCCESS(Status
)) return Status
;
1696 /* Attach to it now */
1697 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1700 /* Lock the address space and make sure the process isn't already dead */
1701 MmLockAddressSpace(&TargetProcess
->Vm
);
1702 if (TargetProcess
->VmDeleted
)
1704 /* Unlock the address space of the process */
1705 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1707 /* Check if we were attached */
1708 if (ProcessHandle
!= NtCurrentProcess())
1710 /* Detach and dereference the process */
1711 KeUnstackDetachProcess(&ApcState
);
1712 ObDereferenceObject(TargetProcess
);
1716 DPRINT1("Process is dying\n");
1717 return STATUS_PROCESS_IS_TERMINATING
;
1721 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1722 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1724 /* Scan on the right */
1725 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1726 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1729 /* Check if this VAD covers the allocation range */
1730 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1731 (BaseVpn
<= Vad
->EndingVpn
))
1738 /* Check if this VAD is too high */
1739 if (BaseVpn
< Vad
->StartingVpn
)
1741 /* Stop if there is no left child */
1742 if (!Vad
->LeftChild
) break;
1744 /* Search on the left next */
1745 Vad
= Vad
->LeftChild
;
1749 /* Then this VAD is too low, keep searching on the right */
1750 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1752 /* Stop if there is no right child */
1753 if (!Vad
->RightChild
) break;
1755 /* Search on the right next */
1756 Vad
= Vad
->RightChild
;
1761 /* Was a VAD found? */
1764 Address
= PAGE_ALIGN(BaseAddress
);
1766 /* Calculate region size */
1769 if (Vad
->StartingVpn
>= BaseVpn
)
1771 /* Region size is the free space till the start of that VAD */
1772 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1776 /* Get the next VAD */
1777 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1780 /* Region size is the free space till the start of that VAD */
1781 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1785 /* Maximum possible region size with that base address */
1786 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1792 /* Maximum possible region size with that base address */
1793 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1796 /* Unlock the address space of the process */
1797 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1799 /* Check if we were attached */
1800 if (ProcessHandle
!= NtCurrentProcess())
1802 /* Detach and derefernece the process */
1803 KeUnstackDetachProcess(&ApcState
);
1804 ObDereferenceObject(TargetProcess
);
1807 /* Build the rest of the initial information block */
1808 MemoryInfo
.BaseAddress
= Address
;
1809 MemoryInfo
.AllocationBase
= NULL
;
1810 MemoryInfo
.AllocationProtect
= 0;
1811 MemoryInfo
.State
= MEM_FREE
;
1812 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1813 MemoryInfo
.Type
= 0;
1815 /* Return the data, NtQueryInformation already probed it*/
1816 if (PreviousMode
!= KernelMode
)
1820 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1821 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1823 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1825 Status
= _SEH2_GetExceptionCode();
1831 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1832 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1838 /* Set the correct memory type based on what kind of VAD this is */
1839 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1840 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1842 MemoryInfo
.Type
= MEM_PRIVATE
;
1844 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1846 MemoryInfo
.Type
= MEM_IMAGE
;
1850 MemoryInfo
.Type
= MEM_MAPPED
;
1853 /* Find the memory area the specified address belongs to */
1854 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1855 ASSERT(MemoryArea
!= NULL
);
1857 /* Determine information dependent on the memory area type */
1858 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1860 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1861 if (!NT_SUCCESS(Status
))
1863 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1864 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1865 NT_ASSERT(NT_SUCCESS(Status
));
1870 /* Build the initial information block */
1871 Address
= PAGE_ALIGN(BaseAddress
);
1872 MemoryInfo
.BaseAddress
= Address
;
1873 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1874 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1875 MemoryInfo
.Type
= MEM_PRIVATE
;
1877 /* Acquire the working set lock (shared is enough) */
1878 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1880 /* Find the largest chunk of memory which has the same state and protection mask */
1881 MemoryInfo
.State
= MiQueryAddressState(Address
,
1884 &MemoryInfo
.Protect
,
1886 Address
= NextAddress
;
1887 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1889 /* Keep going unless the state or protection mask changed */
1890 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1891 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1892 Address
= NextAddress
;
1895 /* Release the working set lock */
1896 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1898 /* Check if we went outside of the VAD */
1899 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1901 /* Set the end of the VAD as the end address */
1902 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1905 /* Now that we know the last VA address, calculate the region size */
1906 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1909 /* Unlock the address space of the process */
1910 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1912 /* Check if we were attached */
1913 if (ProcessHandle
!= NtCurrentProcess())
1915 /* Detach and derefernece the process */
1916 KeUnstackDetachProcess(&ApcState
);
1917 ObDereferenceObject(TargetProcess
);
1920 /* Return the data, NtQueryInformation already probed it */
1921 if (PreviousMode
!= KernelMode
)
1925 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1926 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1928 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1930 Status
= _SEH2_GetExceptionCode();
1936 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1937 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1941 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1942 "State: %lx Type: %lx Size: %lx\n",
1943 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1944 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1945 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1952 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1953 IN ULONG_PTR EndingAddress
,
1955 IN PEPROCESS Process
)
1957 PMMPTE PointerPte
, LastPte
, PointerPde
;
1958 BOOLEAN OnBoundary
= TRUE
;
1961 /* Get the PDE and PTE addresses */
1962 PointerPde
= MiAddressToPde(StartingAddress
);
1963 PointerPte
= MiAddressToPte(StartingAddress
);
1964 LastPte
= MiAddressToPte(EndingAddress
);
1966 /* Loop all the PTEs */
1967 while (PointerPte
<= LastPte
)
1969 /* Check if we've hit an new PDE boundary */
1972 /* Is this PDE demand zero? */
1973 PointerPde
= MiAddressToPte(PointerPte
);
1974 if (PointerPde
->u
.Long
!= 0)
1976 /* It isn't -- is it valid? */
1977 if (PointerPde
->u
.Hard
.Valid
== 0)
1979 /* Nope, fault it in */
1980 PointerPte
= MiPteToAddress(PointerPde
);
1981 MiMakeSystemAddressValid(PointerPte
, Process
);
1986 /* The PTE was already valid, so move to the next one */
1988 PointerPte
= MiPteToAddress(PointerPde
);
1990 /* Is the entire VAD committed? If not, fail */
1991 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1993 /* Everything is committed so far past the range, return true */
1994 if (PointerPte
> LastPte
) return TRUE
;
1998 /* Is the PTE demand zero? */
1999 if (PointerPte
->u
.Long
== 0)
2001 /* Is the entire VAD committed? If not, fail */
2002 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2006 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2007 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
2008 (PointerPte
->u
.Hard
.Valid
== 0) &&
2009 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
2010 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
2012 /* Then part of the range is decommitted, so fail */
2017 /* Move to the next PTE */
2019 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2022 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2028 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2029 IN OUT PVOID
*BaseAddress
,
2030 IN OUT PSIZE_T NumberOfBytesToProtect
,
2031 IN ULONG NewAccessProtection
,
2032 OUT PULONG OldAccessProtection OPTIONAL
)
2034 PMEMORY_AREA MemoryArea
;
2035 PMMSUPPORT AddressSpace
;
2036 ULONG OldAccessProtection_
;
2039 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2040 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2042 AddressSpace
= &Process
->Vm
;
2043 MmLockAddressSpace(AddressSpace
);
2044 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2045 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2047 MmUnlockAddressSpace(AddressSpace
);
2048 return STATUS_UNSUCCESSFUL
;
2051 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2053 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2054 Status
= MmProtectSectionView(AddressSpace
,
2057 *NumberOfBytesToProtect
,
2058 NewAccessProtection
,
2059 OldAccessProtection
);
2061 MmUnlockAddressSpace(AddressSpace
);
2068 MiProtectVirtualMemory(IN PEPROCESS Process
,
2069 IN OUT PVOID
*BaseAddress
,
2070 IN OUT PSIZE_T NumberOfBytesToProtect
,
2071 IN ULONG NewAccessProtection
,
2072 OUT PULONG OldAccessProtection OPTIONAL
)
2074 PMEMORY_AREA MemoryArea
;
2076 PMMSUPPORT AddressSpace
;
2077 ULONG_PTR StartingAddress
, EndingAddress
;
2078 PMMPTE PointerPde
, PointerPte
, LastPte
;
2081 ULONG ProtectionMask
, OldProtect
;
2083 NTSTATUS Status
= STATUS_SUCCESS
;
2084 PETHREAD Thread
= PsGetCurrentThread();
2085 TABLE_SEARCH_RESULT Result
;
2087 /* Calculate base address for the VAD */
2088 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2089 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2091 /* Calculate the protection mask and make sure it's valid */
2092 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2093 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2095 DPRINT1("Invalid protection mask\n");
2096 return STATUS_INVALID_PAGE_PROTECTION
;
2099 /* Check for ROS specific memory area */
2100 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2101 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2104 return MiRosProtectVirtualMemory(Process
,
2106 NumberOfBytesToProtect
,
2107 NewAccessProtection
,
2108 OldAccessProtection
);
2111 /* Lock the address space and make sure the process isn't already dead */
2112 AddressSpace
= MmGetCurrentAddressSpace();
2113 MmLockAddressSpace(AddressSpace
);
2114 if (Process
->VmDeleted
)
2116 DPRINT1("Process is dying\n");
2117 Status
= STATUS_PROCESS_IS_TERMINATING
;
2121 /* Get the VAD for this address range, and make sure it exists */
2122 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2123 EndingAddress
>> PAGE_SHIFT
,
2125 (PMMADDRESS_NODE
*)&Vad
);
2126 if (Result
!= TableFoundNode
)
2128 DPRINT("Could not find a VAD for this allocation\n");
2129 Status
= STATUS_CONFLICTING_ADDRESSES
;
2133 /* Make sure the address is within this VAD's boundaries */
2134 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2135 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2137 Status
= STATUS_CONFLICTING_ADDRESSES
;
2141 /* These kinds of VADs are not supported atm */
2142 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2143 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2144 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2146 DPRINT1("Illegal VAD for attempting to set protection\n");
2147 Status
= STATUS_CONFLICTING_ADDRESSES
;
2151 /* Check for a VAD whose protection can't be changed */
2152 if (Vad
->u
.VadFlags
.NoChange
== 1)
2154 DPRINT1("Trying to change protection of a NoChange VAD\n");
2155 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2159 /* Is this section, or private memory? */
2160 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2162 /* Not yet supported */
2163 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2165 DPRINT1("Illegal VAD for attempting to set protection\n");
2166 Status
= STATUS_CONFLICTING_ADDRESSES
;
2170 /* Rotate VADs are not yet supported */
2171 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2173 DPRINT1("Illegal VAD for attempting to set protection\n");
2174 Status
= STATUS_CONFLICTING_ADDRESSES
;
2178 /* Not valid on section files */
2179 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2182 DPRINT1("Invalid protection flags for section\n");
2183 Status
= STATUS_INVALID_PARAMETER_4
;
2187 /* Check if data or page file mapping protection PTE is compatible */
2188 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2191 DPRINT1("Fixme: Not checking for valid protection\n");
2194 /* This is a section, and this is not yet supported */
2195 DPRINT1("Section protection not yet supported\n");
2200 /* Private memory, check protection flags */
2201 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2202 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2204 DPRINT1("Invalid protection flags for private memory\n");
2205 Status
= STATUS_INVALID_PARAMETER_4
;
2209 /* Lock the working set */
2210 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2212 /* Check if all pages in this range are committed */
2213 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2220 DPRINT1("The entire range is not committed\n");
2221 Status
= STATUS_NOT_COMMITTED
;
2222 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2226 /* Compute starting and ending PTE and PDE addresses */
2227 PointerPde
= MiAddressToPde(StartingAddress
);
2228 PointerPte
= MiAddressToPte(StartingAddress
);
2229 LastPte
= MiAddressToPte(EndingAddress
);
2231 /* Make this PDE valid */
2232 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2234 /* Save protection of the first page */
2235 if (PointerPte
->u
.Long
!= 0)
2237 /* Capture the page protection and make the PDE valid */
2238 OldProtect
= MiGetPageProtection(PointerPte
);
2239 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2243 /* Grab the old protection from the VAD itself */
2244 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2247 /* Loop all the PTEs now */
2248 while (PointerPte
<= LastPte
)
2250 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2251 if (MiIsPteOnPdeBoundary(PointerPte
))
2253 PointerPde
= MiAddressToPte(PointerPte
);
2254 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2257 /* Capture the PTE and check if it was empty */
2258 PteContents
= *PointerPte
;
2259 if (PteContents
.u
.Long
== 0)
2261 /* This used to be a zero PTE and it no longer is, so we must add a
2262 reference to the pagetable. */
2263 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2266 /* Check what kind of PTE we are dealing with */
2267 if (PteContents
.u
.Hard
.Valid
== 1)
2269 /* Get the PFN entry */
2270 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2272 /* We don't support this yet */
2273 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2275 /* Check if the page should not be accessible at all */
2276 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2277 (NewAccessProtection
& PAGE_GUARD
))
2279 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2281 /* Mark the PTE as transition and change its protection */
2282 PteContents
.u
.Hard
.Valid
= 0;
2283 PteContents
.u
.Soft
.Transition
= 1;
2284 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2285 /* Decrease PFN share count and write the PTE */
2286 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2287 // FIXME: remove the page from the WS
2288 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2290 // FIXME: Should invalidate entry in every CPU TLB
2293 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2295 /* We are done for this PTE */
2296 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2300 /* Write the protection mask and write it with a TLB flush */
2301 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2302 MiFlushTbAndCapture(Vad
,
2311 /* We don't support these cases yet */
2312 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2313 //ASSERT(PteContents.u.Soft.Transition == 0);
2315 /* The PTE is already demand-zero, just update the protection mask */
2316 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2317 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2318 ASSERT(PointerPte
->u
.Long
!= 0);
2321 /* Move to the next PTE */
2325 /* Unlock the working set */
2326 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2329 /* Unlock the address space */
2330 MmUnlockAddressSpace(AddressSpace
);
2332 /* Return parameters and success */
2333 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2334 *BaseAddress
= (PVOID
)StartingAddress
;
2335 *OldAccessProtection
= OldProtect
;
2336 return STATUS_SUCCESS
;
2339 /* Unlock the address space and return the failure code */
2340 MmUnlockAddressSpace(AddressSpace
);
2346 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2347 IN PEPROCESS TargetProcess
,
2350 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2353 // Sanity checks. The latter is because we only use this function with the
2354 // PFN lock not held, so it may go away in the future.
2356 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2357 ASSERT(OldIrql
== MM_NOIRQL
);
2360 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2361 // return the same address as the PDE on 2-level page table systems.
2363 // If everything is already valid, there is nothing to do.
2365 PointerPpe
= MiAddressToPte(PointerPde
);
2366 PointerPxe
= MiAddressToPde(PointerPde
);
2367 if ((PointerPxe
->u
.Hard
.Valid
) &&
2368 (PointerPpe
->u
.Hard
.Valid
) &&
2369 (PointerPde
->u
.Hard
.Valid
))
2375 // At least something is invalid, so begin by getting the PTE for the PDE itself
2376 // and then lookup each additional level. We must do it in this precise order
2377 // because the pagfault.c code (as well as in Windows) depends that the next
2378 // level up (higher) must be valid when faulting a lower level
2380 PointerPte
= MiPteToAddress(PointerPde
);
2384 // Make sure APCs continued to be disabled
2386 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2389 // First, make the PXE valid if needed
2391 if (!PointerPxe
->u
.Hard
.Valid
)
2393 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2394 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2400 if (!PointerPpe
->u
.Hard
.Valid
)
2402 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2403 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2407 // And finally, make the PDE itself valid.
2409 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2412 // This should've worked the first time so the loop is really just for
2413 // show -- ASSERT that we're actually NOT going to be looping.
2415 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2416 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2417 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2418 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2419 !(PointerPpe
->u
.Hard
.Valid
) ||
2420 !(PointerPde
->u
.Hard
.Valid
));
2425 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2431 PFN_NUMBER PageFrameIndex
;
2435 // Acquire the PFN lock and loop all the PTEs in the list
2437 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2438 for (i
= 0; i
!= Count
; i
++)
2441 // The PTE must currently be valid
2443 TempPte
= *ValidPteList
[i
];
2444 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2447 // Get the PFN entry for the page itself, and then for its page table
2449 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2450 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2451 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2454 // Decrement the share count on the page table, and then on the page
2457 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2458 MI_SET_PFN_DELETED(Pfn1
);
2459 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2462 // Make the page decommitted
2464 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2468 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2469 // and then release the PFN lock
2472 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2477 MiDecommitPages(IN PVOID StartingAddress
,
2478 IN PMMPTE EndingPte
,
2479 IN PEPROCESS Process
,
2482 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2483 ULONG CommitReduction
= 0;
2484 PMMPTE ValidPteList
[256];
2488 PETHREAD CurrentThread
= PsGetCurrentThread();
2491 // Get the PTE and PTE for the address, and lock the working set
2492 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2493 // commited range ends so that we can do the right accounting.
2495 PointerPde
= MiAddressToPde(StartingAddress
);
2496 PointerPte
= MiAddressToPte(StartingAddress
);
2497 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2498 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2501 // Make the PDE valid, and now loop through each page's worth of data
2503 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2504 while (PointerPte
<= EndingPte
)
2507 // Check if we've crossed a PDE boundary
2509 if (MiIsPteOnPdeBoundary(PointerPte
))
2512 // Get the new PDE and flush the valid PTEs we had built up until
2513 // now. This helps reduce the amount of TLB flushing we have to do.
2514 // Note that Windows does a much better job using timestamps and
2515 // such, and does not flush the entire TLB all the time, but right
2516 // now we have bigger problems to worry about than TLB flushing.
2518 PointerPde
= MiAddressToPde(StartingAddress
);
2521 MiProcessValidPteList(ValidPteList
, PteCount
);
2526 // Make this PDE valid
2528 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2532 // Read this PTE. It might be active or still demand-zero.
2534 PteContents
= *PointerPte
;
2535 if (PteContents
.u
.Long
)
2538 // The PTE is active. It might be valid and in a working set, or
2539 // it might be a prototype PTE or paged out or even in transition.
2541 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2544 // It's already decommited, so there's nothing for us to do here
2551 // Remove it from the counters, and check if it was valid or not
2553 //Process->NumberOfPrivatePages--;
2554 if (PteContents
.u
.Hard
.Valid
)
2557 // It's valid. At this point make sure that it is not a ROS
2558 // PFN. Also, we don't support ProtoPTEs in this code path.
2560 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2561 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2562 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2565 // Flush any pending PTEs that we had not yet flushed, if our
2566 // list has gotten too big, then add this PTE to the flush list.
2568 if (PteCount
== 256)
2570 MiProcessValidPteList(ValidPteList
, PteCount
);
2573 ValidPteList
[PteCount
++] = PointerPte
;
2578 // We do not support any of these other scenarios at the moment
2580 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2581 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2582 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2585 // So the only other possibility is that it is still a demand
2586 // zero PTE, in which case we undo the accounting we did
2587 // earlier and simply make the page decommitted.
2589 //Process->NumberOfPrivatePages++;
2590 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2597 // This used to be a zero PTE and it no longer is, so we must add a
2598 // reference to the pagetable.
2600 MiIncrementPageTableReferences(StartingAddress
);
2603 // Next, we account for decommitted PTEs and make the PTE as such
2605 if (PointerPte
> CommitPte
) CommitReduction
++;
2606 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2610 // Move to the next PTE and the next address
2613 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2617 // Flush any dangling PTEs from the loop in the last page table, and then
2618 // release the working set and return the commit reduction accounting.
2620 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2621 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2622 return CommitReduction
;
2625 /* PUBLIC FUNCTIONS ***********************************************************/
2632 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2643 MmSecureVirtualMemory(IN PVOID Address
,
2647 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2656 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2658 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2661 /* SYSTEM CALLS ***************************************************************/
2665 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2666 IN PVOID BaseAddress
,
2668 IN SIZE_T NumberOfBytesToRead
,
2669 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2671 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2673 NTSTATUS Status
= STATUS_SUCCESS
;
2674 SIZE_T BytesRead
= 0;
2678 // Check if we came from user mode
2680 if (PreviousMode
!= KernelMode
)
2683 // Validate the read addresses
2685 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2686 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2687 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2688 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2691 // Don't allow to write into kernel space
2693 return STATUS_ACCESS_VIOLATION
;
2697 // Enter SEH for probe
2702 // Probe the output value
2704 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2709 // Get exception code
2711 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2717 // Don't do zero-byte transfers
2719 if (NumberOfBytesToRead
)
2722 // Reference the process
2724 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2730 if (NT_SUCCESS(Status
))
2735 Status
= MmCopyVirtualMemory(Process
,
2737 PsGetCurrentProcess(),
2739 NumberOfBytesToRead
,
2744 // Dereference the process
2746 ObDereferenceObject(Process
);
2751 // Check if the caller sent this parameter
2753 if (NumberOfBytesRead
)
2756 // Enter SEH to guard write
2761 // Return the number of bytes read
2763 *NumberOfBytesRead
= BytesRead
;
2765 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2779 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2780 IN PVOID BaseAddress
,
2782 IN SIZE_T NumberOfBytesToWrite
,
2783 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2785 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2787 NTSTATUS Status
= STATUS_SUCCESS
;
2788 SIZE_T BytesWritten
= 0;
2792 // Check if we came from user mode
2794 if (PreviousMode
!= KernelMode
)
2797 // Validate the read addresses
2799 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2800 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2801 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2802 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2805 // Don't allow to write into kernel space
2807 return STATUS_ACCESS_VIOLATION
;
2811 // Enter SEH for probe
2816 // Probe the output value
2818 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2820 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2823 // Get exception code
2825 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2831 // Don't do zero-byte transfers
2833 if (NumberOfBytesToWrite
)
2836 // Reference the process
2838 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2844 if (NT_SUCCESS(Status
))
2849 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2853 NumberOfBytesToWrite
,
2858 // Dereference the process
2860 ObDereferenceObject(Process
);
2865 // Check if the caller sent this parameter
2867 if (NumberOfBytesWritten
)
2870 // Enter SEH to guard write
2875 // Return the number of bytes written
2877 *NumberOfBytesWritten
= BytesWritten
;
2879 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2893 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2894 IN OUT PVOID
*UnsafeBaseAddress
,
2895 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2896 IN ULONG NewAccessProtection
,
2897 OUT PULONG UnsafeOldAccessProtection
)
2900 ULONG OldAccessProtection
;
2902 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2903 PVOID BaseAddress
= NULL
;
2904 SIZE_T NumberOfBytesToProtect
= 0;
2905 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2907 BOOLEAN Attached
= FALSE
;
2908 KAPC_STATE ApcState
;
2912 // Check for valid protection flags
2914 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2915 if (Protection
!= PAGE_NOACCESS
&&
2916 Protection
!= PAGE_READONLY
&&
2917 Protection
!= PAGE_READWRITE
&&
2918 Protection
!= PAGE_WRITECOPY
&&
2919 Protection
!= PAGE_EXECUTE
&&
2920 Protection
!= PAGE_EXECUTE_READ
&&
2921 Protection
!= PAGE_EXECUTE_READWRITE
&&
2922 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2927 return STATUS_INVALID_PAGE_PROTECTION
;
2931 // Check if we came from user mode
2933 if (PreviousMode
!= KernelMode
)
2936 // Enter SEH for probing
2941 // Validate all outputs
2943 ProbeForWritePointer(UnsafeBaseAddress
);
2944 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2945 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2950 BaseAddress
= *UnsafeBaseAddress
;
2951 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2953 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2956 // Get exception code
2958 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2967 BaseAddress
= *UnsafeBaseAddress
;
2968 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2972 // Catch illegal base address
2974 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2977 // Catch illegal region size
2979 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2984 return STATUS_INVALID_PARAMETER_3
;
2988 // 0 is also illegal
2990 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2993 // Get a reference to the process
2995 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2996 PROCESS_VM_OPERATION
,
3001 if (!NT_SUCCESS(Status
)) return Status
;
3004 // Check if we should attach
3006 if (CurrentProcess
!= Process
)
3011 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3016 // Do the actual work
3018 Status
= MiProtectVirtualMemory(Process
,
3020 &NumberOfBytesToProtect
,
3021 NewAccessProtection
,
3022 &OldAccessProtection
);
3027 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3030 // Release reference
3032 ObDereferenceObject(Process
);
3035 // Enter SEH to return data
3040 // Return data to user
3042 *UnsafeOldAccessProtection
= OldAccessProtection
;
3043 *UnsafeBaseAddress
= BaseAddress
;
3044 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3046 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3063 // HACK until we have proper WSLIST support
3064 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3066 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3068 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3080 // HACK until we have proper WSLIST support
3081 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3083 if (!Wsle
->u1
.e1
.LockedInWs
&&
3084 !Wsle
->u1
.e1
.LockedInMemory
)
3086 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3089 if (LockType
& MAP_PROCESS
)
3090 Wsle
->u1
.e1
.LockedInWs
= 1;
3091 if (LockType
& MAP_SYSTEM
)
3092 Wsle
->u1
.e1
.LockedInMemory
= 1;
3101 // HACK until we have proper WSLIST support
3102 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3104 if (LockType
& MAP_PROCESS
)
3105 Wsle
->u1
.e1
.LockedInWs
= 0;
3106 if (LockType
& MAP_SYSTEM
)
3107 Wsle
->u1
.e1
.LockedInMemory
= 0;
3109 if (!Wsle
->u1
.e1
.LockedInWs
&&
3110 !Wsle
->u1
.e1
.LockedInMemory
)
3112 MiDereferencePfnAndDropLockCount(Pfn1
);
3118 MiCheckVadsForLockOperation(
3119 _Inout_ PVOID
*BaseAddress
,
3120 _Inout_ PSIZE_T RegionSize
,
3121 _Inout_ PVOID
*EndAddress
)
3127 /* Get the base address and align the start address */
3128 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3129 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3130 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3132 /* First loop and check all VADs */
3133 CurrentVa
= *BaseAddress
;
3134 while (CurrentVa
< *EndAddress
)
3137 Vad
= MiLocateAddress(CurrentVa
);
3140 /// FIXME: this might be a memory area for a section view...
3141 return STATUS_ACCESS_VIOLATION
;
3144 /* Check VAD type */
3145 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3146 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3147 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3149 *EndAddress
= CurrentVa
;
3150 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3151 return STATUS_INCOMPATIBLE_FILE_MAP
;
3154 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3157 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3158 return STATUS_SUCCESS
;
3163 MiLockVirtualMemory(
3164 IN OUT PVOID
*BaseAddress
,
3165 IN OUT PSIZE_T RegionSize
,
3168 PEPROCESS CurrentProcess
;
3169 PMMSUPPORT AddressSpace
;
3170 PVOID CurrentVa
, EndAddress
;
3171 PMMPTE PointerPte
, LastPte
;
3173 #if (_MI_PAGING_LEVELS >= 3)
3176 #if (_MI_PAGING_LEVELS == 4)
3180 NTSTATUS Status
, TempStatus
;
3182 /* Lock the address space */
3183 AddressSpace
= MmGetCurrentAddressSpace();
3184 MmLockAddressSpace(AddressSpace
);
3186 /* Make sure we still have an address space */
3187 CurrentProcess
= PsGetCurrentProcess();
3188 if (CurrentProcess
->VmDeleted
)
3190 Status
= STATUS_PROCESS_IS_TERMINATING
;
3194 /* Check the VADs in the requested range */
3195 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3196 if (!NT_SUCCESS(Status
))
3201 /* Enter SEH for probing */
3204 /* Loop all pages and probe them */
3205 CurrentVa
= *BaseAddress
;
3206 while (CurrentVa
< EndAddress
)
3208 (void)(*(volatile CHAR
*)CurrentVa
);
3209 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3214 Status
= _SEH2_GetExceptionCode();
3219 /* All pages were accessible, since we hold the address space lock, nothing
3220 can be de-committed. Assume success for now. */
3221 Status
= STATUS_SUCCESS
;
3223 /* Get the PTE and PDE */
3224 PointerPte
= MiAddressToPte(*BaseAddress
);
3225 PointerPde
= MiAddressToPde(*BaseAddress
);
3226 #if (_MI_PAGING_LEVELS >= 3)
3227 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3229 #if (_MI_PAGING_LEVELS == 4)
3230 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3233 /* Get the last PTE */
3234 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3236 /* Lock the process working set */
3237 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3239 /* Loop the pages */
3242 /* Check for a page that is not accessible */
3244 #if (_MI_PAGING_LEVELS == 4)
3245 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3247 #if (_MI_PAGING_LEVELS >= 3)
3248 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3250 (PointerPde
->u
.Hard
.Valid
== 0) ||
3251 (PointerPte
->u
.Hard
.Valid
== 0))
3253 /* Release process working set */
3254 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3256 /* Access the page */
3257 CurrentVa
= MiPteToAddress(PointerPte
);
3259 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3260 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3261 if (!NT_SUCCESS(TempStatus
))
3263 // This should only happen, when remote backing storage is not accessible
3265 Status
= TempStatus
;
3269 /* Lock the process working set */
3270 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3274 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3275 ASSERT(Pfn1
!= NULL
);
3277 /* Check the previous lock status */
3278 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3280 Status
= STATUS_WAS_LOCKED
;
3284 MI_LOCK_VA(Pfn1
, MapType
);
3286 /* Go to the next PTE */
3289 /* Check if we're on a PDE boundary */
3290 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3291 #if (_MI_PAGING_LEVELS >= 3)
3292 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3294 #if (_MI_PAGING_LEVELS == 4)
3295 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3297 } while (PointerPte
<= LastPte
);
3299 /* Release process working set */
3300 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3303 /* Unlock address space */
3304 MmUnlockAddressSpace(AddressSpace
);
3311 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3312 IN OUT PVOID
*BaseAddress
,
3313 IN OUT PSIZE_T NumberOfBytesToLock
,
3317 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3319 BOOLEAN Attached
= FALSE
;
3320 KAPC_STATE ApcState
;
3321 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3322 PVOID CapturedBaseAddress
;
3323 SIZE_T CapturedBytesToLock
;
3329 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3332 // Invalid set of flags
3334 return STATUS_INVALID_PARAMETER
;
3338 // At least one flag must be specified
3340 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3345 return STATUS_INVALID_PARAMETER
;
3349 // Enter SEH for probing
3354 // Validate output data
3356 ProbeForWritePointer(BaseAddress
);
3357 ProbeForWriteSize_t(NumberOfBytesToLock
);
3362 CapturedBaseAddress
= *BaseAddress
;
3363 CapturedBytesToLock
= *NumberOfBytesToLock
;
3365 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3368 // Get exception code
3370 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3375 // Catch illegal base address
3377 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3380 // Catch illegal region size
3382 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3387 return STATUS_INVALID_PARAMETER
;
3391 // 0 is also illegal
3393 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3396 // Get a reference to the process
3398 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3399 PROCESS_VM_OPERATION
,
3404 if (!NT_SUCCESS(Status
)) return Status
;
3407 // Check if this is is system-mapped
3409 if (MapType
& MAP_SYSTEM
)
3412 // Check for required privilege