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 /* We only support valid PTEs for now */
408 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
409 if (TempPte
.u
.Hard
.Valid
== 0)
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
413 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde
= MiPteToPde(PointerPte
);
425 if (PointerPde
->u
.Hard
.Valid
== 0)
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT
,
435 (ULONG_PTR
)PointerPte
,
437 (ULONG_PTR
)VirtualAddress
);
439 #if (_MI_PAGING_LEVELS == 2)
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
450 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT
,
455 (ULONG_PTR
)PointerPte
,
456 (ULONG_PTR
)PrototypePte
,
457 (ULONG_PTR
)Pfn1
->PteAddress
);
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT
,
469 (ULONG_PTR
)PointerPte
,
471 (ULONG_PTR
)Pfn1
->PteAddress
);
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1
->u2
.ShareCount
== 1);
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1
);
482 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
488 /* Destroy the PTE and flush the TLB */
489 MI_ERASE_PTE(PointerPte
);
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
496 IN ULONG_PTR EndingAddress
,
499 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
502 PEPROCESS CurrentProcess
;
504 BOOLEAN AddressGap
= FALSE
;
505 PSUBSECTION Subsection
;
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess
= PsGetCurrentProcess();
512 PointerPde
= MiAddressToPde(Va
);
513 PointerPte
= MiAddressToPte(Va
);
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
518 /* Don't worry about prototypes */
519 PrototypePte
= LastPrototypePte
= NULL
;
523 /* Get the prototype PTE */
524 PrototypePte
= Vad
->FirstPrototypePte
;
525 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
531 /* Loop the PTE for each VA */
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde
->u
.Long
)
537 /* There are gaps in the address space */
540 /* Still no valid PDE, try the next 4MB (or whatever) */
543 /* Update the PTE on this new boundary */
544 PointerPte
= MiPteToAddress(PointerPde
);
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
548 if (Va
> EndingAddress
) return;
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde
->u
.Hard
.Valid
)
554 /* It isn't, so map it in */
555 PointerPte
= MiPteToAddress(PointerPde
);
556 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
561 ASSERT(Va
<= EndingAddress
);
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap
) && (LastPrototypePte
))
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
574 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
578 /* No more subsections, we are done with prototype PTEs */
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
587 /* Capture the PDE and make sure it exists */
588 TempPte
= *PointerPte
;
591 MiDecrementPageTableReferences((PVOID
)Va
);
593 /* Check if the PTE is actually mapped in */
594 if (MI_IS_MAPPED_PTE(&TempPte
))
596 /* Are we dealing with section VAD? */
597 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
599 /* We need to skip to the next correct prototype PTE */
600 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
602 /* And we need the subsection to skip to the next last prototype PTE */
603 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
607 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
611 /* No more subsections, we are done with prototype PTEs */
616 /* Check for prototype PTE */
617 if ((TempPte
.u
.Hard
.Valid
== 0) &&
618 (TempPte
.u
.Soft
.Prototype
== 1))
621 MI_ERASE_PTE(PointerPte
);
625 /* Delete the PTE proper */
626 MiDeletePte(PointerPte
,
634 /* The PTE was never mapped, just nuke it here */
635 MI_ERASE_PTE(PointerPte
);
639 /* Update the address and PTE for it */
644 /* Making sure the PDE is still valid */
645 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
647 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
649 /* The PDE should still be valid at this point */
650 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
652 /* Check remaining PTE count (go back 1 page due to above loop) */
653 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
655 if (PointerPde
->u
.Long
!= 0)
657 /* Delete the PTE proper */
658 MiDeletePte(PointerPde
,
659 MiPteToAddress(PointerPde
),
665 /* Release the lock and get out if we're done */
666 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
667 if (Va
> EndingAddress
) return;
669 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
670 PointerPde
= MiAddressToPde(Va
);
676 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
677 OUT PBOOLEAN HaveBadAddress
,
678 OUT PULONG_PTR BadAddress
)
680 PEXCEPTION_RECORD ExceptionRecord
;
686 *HaveBadAddress
= FALSE
;
689 // Get the exception record
691 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
694 // Look at the exception code
696 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
697 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
698 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
701 // We can tell the address if we have more than one parameter
703 if (ExceptionRecord
->NumberParameters
> 1)
706 // Return the address
708 *HaveBadAddress
= TRUE
;
709 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
714 // Continue executing the next handler
716 return EXCEPTION_EXECUTE_HANDLER
;
721 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
722 IN PVOID SourceAddress
,
723 IN PEPROCESS TargetProcess
,
724 OUT PVOID TargetAddress
,
725 IN SIZE_T BufferSize
,
726 IN KPROCESSOR_MODE PreviousMode
,
727 OUT PSIZE_T ReturnSize
)
729 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
730 PMDL Mdl
= (PMDL
)MdlBuffer
;
731 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
732 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
733 volatile BOOLEAN PagesLocked
;
734 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
735 volatile PVOID MdlAddress
;
737 BOOLEAN HaveBadAddress
;
738 ULONG_PTR BadAddress
;
739 NTSTATUS Status
= STATUS_SUCCESS
;
743 // Calculate the maximum amount of data to move
745 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
746 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
747 CurrentSize
= TotalSize
;
748 RemainingSize
= BufferSize
;
751 // Loop as long as there is still data
753 while (RemainingSize
> 0)
756 // Check if this transfer will finish everything off
758 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
761 // Attach to the source address space
763 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
766 // Reset state for this pass
770 FailedInMoving
= FALSE
;
771 ASSERT(FailedInProbe
== FALSE
);
774 // Protect user-mode copy
779 // If this is our first time, probe the buffer
781 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
784 // Catch a failure here
786 FailedInProbe
= TRUE
;
791 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
796 FailedInProbe
= FALSE
;
800 // Initialize and probe and lock the MDL
802 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
803 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
809 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
818 // Use our SEH handler to pick this up
820 FailedInMapping
= TRUE
;
821 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
825 // Now let go of the source and grab to the target process
827 KeUnstackDetachProcess(&ApcState
);
828 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
831 // Check if this is our first time through
833 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
836 // Catch a failure here
838 FailedInProbe
= TRUE
;
843 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
848 FailedInProbe
= FALSE
;
852 // Now do the actual move
854 FailedInMoving
= TRUE
;
855 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
857 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
862 // Detach from whoever we may be attached to
864 KeUnstackDetachProcess(&ApcState
);
867 // Check if we had mapped the pages
869 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
872 // Check if we had locked the pages
874 if (PagesLocked
) MmUnlockPages(Mdl
);
877 // Check if we hit working set quota
879 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
884 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
888 // Check if we failed during the probe or mapping
890 if ((FailedInProbe
) || (FailedInMapping
))
895 Status
= _SEH2_GetExceptionCode();
896 _SEH2_YIELD(return Status
);
900 // Otherwise, we failed probably during the move
902 *ReturnSize
= BufferSize
- RemainingSize
;
906 // Check if we know exactly where we stopped copying
911 // Return the exact number of bytes copied
913 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
918 // Return partial copy
920 Status
= STATUS_PARTIAL_COPY
;
925 // Check for SEH status
927 if (Status
!= STATUS_SUCCESS
) return Status
;
930 // Detach from target
932 KeUnstackDetachProcess(&ApcState
);
937 MmUnmapLockedPages(MdlAddress
, Mdl
);
941 // Update location and size
943 RemainingSize
-= CurrentSize
;
944 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
945 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
951 *ReturnSize
= BufferSize
;
952 return STATUS_SUCCESS
;
957 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
958 IN PVOID SourceAddress
,
959 IN PEPROCESS TargetProcess
,
960 OUT PVOID TargetAddress
,
961 IN SIZE_T BufferSize
,
962 IN KPROCESSOR_MODE PreviousMode
,
963 OUT PSIZE_T ReturnSize
)
965 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
966 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
967 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
968 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
971 BOOLEAN HaveBadAddress
;
972 ULONG_PTR BadAddress
;
973 NTSTATUS Status
= STATUS_SUCCESS
;
977 // Calculate the maximum amount of data to move
979 TotalSize
= MI_MAX_TRANSFER_SIZE
;
980 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
981 CurrentSize
= TotalSize
;
982 RemainingSize
= BufferSize
;
985 // Check if we can use the stack
987 if (BufferSize
<= MI_POOL_COPY_BYTES
)
992 PoolAddress
= (PVOID
)StackBuffer
;
999 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1000 if (!PoolAddress
) ASSERT(FALSE
);
1001 HavePoolAddress
= TRUE
;
1005 // Loop as long as there is still data
1007 while (RemainingSize
> 0)
1010 // Check if this transfer will finish everything off
1012 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1015 // Attach to the source address space
1017 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1020 // Reset state for this pass
1022 FailedInMoving
= FALSE
;
1023 ASSERT(FailedInProbe
== FALSE
);
1026 // Protect user-mode copy
1031 // If this is our first time, probe the buffer
1033 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1036 // Catch a failure here
1038 FailedInProbe
= TRUE
;
1043 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1048 FailedInProbe
= FALSE
;
1054 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1057 // Now let go of the source and grab to the target process
1059 KeUnstackDetachProcess(&ApcState
);
1060 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1063 // Check if this is our first time through
1065 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1068 // Catch a failure here
1070 FailedInProbe
= TRUE
;
1075 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1080 FailedInProbe
= FALSE
;
1084 // Now do the actual move
1086 FailedInMoving
= TRUE
;
1087 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1089 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1094 // Detach from whoever we may be attached to
1096 KeUnstackDetachProcess(&ApcState
);
1099 // Check if we had allocated pool
1101 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1104 // Check if we failed during the probe
1111 Status
= _SEH2_GetExceptionCode();
1112 _SEH2_YIELD(return Status
);
1116 // Otherwise, we failed, probably during the move
1118 *ReturnSize
= BufferSize
- RemainingSize
;
1122 // Check if we know exactly where we stopped copying
1127 // Return the exact number of bytes copied
1129 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1134 // Return partial copy
1136 Status
= STATUS_PARTIAL_COPY
;
1141 // Check for SEH status
1143 if (Status
!= STATUS_SUCCESS
) return Status
;
1146 // Detach from target
1148 KeUnstackDetachProcess(&ApcState
);
1151 // Update location and size
1153 RemainingSize
-= CurrentSize
;
1154 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1155 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1160 // Check if we had allocated pool
1162 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1167 *ReturnSize
= BufferSize
;
1168 return STATUS_SUCCESS
;
1173 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1174 IN PVOID SourceAddress
,
1175 IN PEPROCESS TargetProcess
,
1176 OUT PVOID TargetAddress
,
1177 IN SIZE_T BufferSize
,
1178 IN KPROCESSOR_MODE PreviousMode
,
1179 OUT PSIZE_T ReturnSize
)
1182 PEPROCESS Process
= SourceProcess
;
1185 // Don't accept zero-sized buffers
1187 if (!BufferSize
) return STATUS_SUCCESS
;
1190 // If we are copying from ourselves, lock the target instead
1192 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1195 // Acquire rundown protection
1197 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1202 return STATUS_PROCESS_IS_TERMINATING
;
1206 // See if we should use the pool copy
1208 if (BufferSize
> MI_POOL_COPY_BYTES
)
1213 Status
= MiDoMappedCopy(SourceProcess
,
1226 Status
= MiDoPoolCopy(SourceProcess
,
1238 ExReleaseRundownProtection(&Process
->RundownProtect
);
1244 MmFlushVirtualMemory(IN PEPROCESS Process
,
1245 IN OUT PVOID
*BaseAddress
,
1246 IN OUT PSIZE_T RegionSize
,
1247 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1255 return STATUS_SUCCESS
;
1260 MiGetPageProtection(IN PMMPTE PointerPte
)
1266 /* Copy this PTE's contents */
1267 TempPte
= *PointerPte
;
1269 /* Assure it's not totally zero */
1270 ASSERT(TempPte
.u
.Long
);
1272 /* Check for a special prototype format */
1273 if (TempPte
.u
.Soft
.Valid
== 0 &&
1274 TempPte
.u
.Soft
.Prototype
== 1)
1276 /* Unsupported now */
1281 /* In the easy case of transition or demand zero PTE just return its protection */
1282 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1284 /* If we get here, the PTE is valid, so look up the page in PFN database */
1285 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1286 if (!Pfn
->u3
.e1
.PrototypePte
)
1288 /* Return protection of the original pte */
1289 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1290 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1293 /* This is software PTE */
1294 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1295 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1296 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1297 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1298 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1303 MiQueryAddressState(IN PVOID Va
,
1305 IN PEPROCESS TargetProcess
,
1306 OUT PULONG ReturnedProtect
,
1310 PMMPTE PointerPte
, ProtoPte
;
1312 #if (_MI_PAGING_LEVELS >= 3)
1315 #if (_MI_PAGING_LEVELS >= 4)
1318 MMPTE TempPte
, TempProtoPte
;
1319 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1320 ULONG State
= MEM_RESERVE
, Protect
= 0;
1321 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1322 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1324 /* Only normal VADs supported */
1325 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1327 /* Get the PDE and PTE for the address */
1328 PointerPde
= MiAddressToPde(Va
);
1329 PointerPte
= MiAddressToPte(Va
);
1330 #if (_MI_PAGING_LEVELS >= 3)
1331 PointerPpe
= MiAddressToPpe(Va
);
1333 #if (_MI_PAGING_LEVELS >= 4)
1334 PointerPxe
= MiAddressToPxe(Va
);
1337 /* Return the next range */
1338 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1342 #if (_MI_PAGING_LEVELS >= 4)
1343 /* Does the PXE exist? */
1344 if (PointerPxe
->u
.Long
== 0)
1346 /* It does not, next range starts at the next PXE */
1347 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1351 /* Is the PXE valid? */
1352 if (PointerPxe
->u
.Hard
.Valid
== 0)
1354 /* Is isn't, fault it in (make the PPE accessible) */
1355 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1358 #if (_MI_PAGING_LEVELS >= 3)
1359 /* Does the PPE exist? */
1360 if (PointerPpe
->u
.Long
== 0)
1362 /* It does not, next range starts at the next PPE */
1363 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1367 /* Is the PPE valid? */
1368 if (PointerPpe
->u
.Hard
.Valid
== 0)
1370 /* Is isn't, fault it in (make the PDE accessible) */
1371 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1375 /* Does the PDE exist? */
1376 if (PointerPde
->u
.Long
== 0)
1378 /* It does not, next range starts at the next PDE */
1379 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1383 /* Is the PDE valid? */
1384 if (PointerPde
->u
.Hard
.Valid
== 0)
1386 /* Is isn't, fault it in (make the PTE accessible) */
1387 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1390 /* We have a PTE that we can access now! */
1395 /* Is it safe to try reading the PTE? */
1398 /* FIXME: watch out for large pages */
1399 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1401 /* Capture the PTE */
1402 TempPte
= *PointerPte
;
1403 if (TempPte
.u
.Long
!= 0)
1405 /* The PTE is valid, so it's not zeroed out */
1406 DemandZeroPte
= FALSE
;
1408 /* Is it a decommited, invalid, or faulted PTE? */
1409 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1410 (TempPte
.u
.Hard
.Valid
== 0) &&
1411 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1412 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1414 /* Otherwise our defaults should hold */
1415 ASSERT(Protect
== 0);
1416 ASSERT(State
== MEM_RESERVE
);
1420 /* This means it's committed */
1423 /* We don't support these */
1424 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1425 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1426 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1428 /* Get protection state of this page */
1429 Protect
= MiGetPageProtection(PointerPte
);
1431 /* Check if this is an image-backed VAD */
1432 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1433 (TempPte
.u
.Soft
.Prototype
== 1) &&
1434 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1437 DPRINT1("Not supported\n");
1444 /* Check if this was a demand-zero PTE, since we need to find the state */
1447 /* Not yet handled */
1448 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1449 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1451 /* Check if this is private commited memory, or an section-backed VAD */
1452 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1454 /* Tell caller about the next range */
1455 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1457 /* Get the prototype PTE for this VAD */
1458 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1459 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1462 /* We should unlock the working set, but it's not being held! */
1464 /* Is the prototype PTE actually valid (committed)? */
1465 TempProtoPte
= *ProtoPte
;
1466 if (TempProtoPte
.u
.Long
)
1468 /* Unless this is a memory-mapped file, handle it like private VAD */
1470 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1471 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1474 /* We should re-lock the working set */
1477 else if (Vad
->u
.VadFlags
.MemCommit
)
1479 /* This is committed memory */
1482 /* Convert the protection */
1483 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1487 /* Return the protection code */
1488 *ReturnedProtect
= Protect
;
1494 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1495 IN PVOID BaseAddress
,
1496 OUT PVOID MemoryInformation
,
1497 IN SIZE_T MemoryInformationLength
,
1498 OUT PSIZE_T ReturnLength
)
1500 PEPROCESS TargetProcess
;
1501 NTSTATUS Status
= STATUS_SUCCESS
;
1503 PVOID Address
, NextAddress
;
1504 BOOLEAN Found
= FALSE
;
1505 ULONG NewProtect
, NewState
;
1507 MEMORY_BASIC_INFORMATION MemoryInfo
;
1508 KAPC_STATE ApcState
;
1509 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1510 PMEMORY_AREA MemoryArea
;
1511 SIZE_T ResultLength
;
1513 /* Check for illegal addresses in user-space, or the shared memory area */
1514 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1515 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1517 Address
= PAGE_ALIGN(BaseAddress
);
1519 /* Make up an info structure describing this range */
1520 MemoryInfo
.BaseAddress
= Address
;
1521 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1522 MemoryInfo
.Type
= MEM_PRIVATE
;
1524 /* Special case for shared data */
1525 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1527 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1528 MemoryInfo
.State
= MEM_COMMIT
;
1529 MemoryInfo
.Protect
= PAGE_READONLY
;
1530 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1534 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1535 MemoryInfo
.State
= MEM_RESERVE
;
1536 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1537 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1540 /* Return the data, NtQueryInformation already probed it*/
1541 if (PreviousMode
!= KernelMode
)
1545 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1546 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1548 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1550 Status
= _SEH2_GetExceptionCode();
1556 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1557 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1563 /* Check if this is for a local or remote process */
1564 if (ProcessHandle
== NtCurrentProcess())
1566 TargetProcess
= PsGetCurrentProcess();
1570 /* Reference the target process */
1571 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1572 PROCESS_QUERY_INFORMATION
,
1574 ExGetPreviousMode(),
1575 (PVOID
*)&TargetProcess
,
1577 if (!NT_SUCCESS(Status
)) return Status
;
1579 /* Attach to it now */
1580 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1583 /* Lock the address space and make sure the process isn't already dead */
1584 MmLockAddressSpace(&TargetProcess
->Vm
);
1585 if (TargetProcess
->VmDeleted
)
1587 /* Unlock the address space of the process */
1588 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1590 /* Check if we were attached */
1591 if (ProcessHandle
!= NtCurrentProcess())
1593 /* Detach and dereference the process */
1594 KeUnstackDetachProcess(&ApcState
);
1595 ObDereferenceObject(TargetProcess
);
1599 DPRINT1("Process is dying\n");
1600 return STATUS_PROCESS_IS_TERMINATING
;
1604 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1605 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1607 /* Scan on the right */
1608 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1609 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1612 /* Check if this VAD covers the allocation range */
1613 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1614 (BaseVpn
<= Vad
->EndingVpn
))
1621 /* Check if this VAD is too high */
1622 if (BaseVpn
< Vad
->StartingVpn
)
1624 /* Stop if there is no left child */
1625 if (!Vad
->LeftChild
) break;
1627 /* Search on the left next */
1628 Vad
= Vad
->LeftChild
;
1632 /* Then this VAD is too low, keep searching on the right */
1633 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1635 /* Stop if there is no right child */
1636 if (!Vad
->RightChild
) break;
1638 /* Search on the right next */
1639 Vad
= Vad
->RightChild
;
1644 /* Was a VAD found? */
1647 Address
= PAGE_ALIGN(BaseAddress
);
1649 /* Calculate region size */
1652 if (Vad
->StartingVpn
>= BaseVpn
)
1654 /* Region size is the free space till the start of that VAD */
1655 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1659 /* Get the next VAD */
1660 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1663 /* Region size is the free space till the start of that VAD */
1664 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1668 /* Maximum possible region size with that base address */
1669 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1675 /* Maximum possible region size with that base address */
1676 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1679 /* Unlock the address space of the process */
1680 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1682 /* Check if we were attached */
1683 if (ProcessHandle
!= NtCurrentProcess())
1685 /* Detach and derefernece the process */
1686 KeUnstackDetachProcess(&ApcState
);
1687 ObDereferenceObject(TargetProcess
);
1690 /* Build the rest of the initial information block */
1691 MemoryInfo
.BaseAddress
= Address
;
1692 MemoryInfo
.AllocationBase
= NULL
;
1693 MemoryInfo
.AllocationProtect
= 0;
1694 MemoryInfo
.State
= MEM_FREE
;
1695 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1696 MemoryInfo
.Type
= 0;
1698 /* Return the data, NtQueryInformation already probed it*/
1699 if (PreviousMode
!= KernelMode
)
1703 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1704 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1708 Status
= _SEH2_GetExceptionCode();
1714 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1715 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1721 /* Set the correct memory type based on what kind of VAD this is */
1722 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1723 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1725 MemoryInfo
.Type
= MEM_PRIVATE
;
1727 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1729 MemoryInfo
.Type
= MEM_IMAGE
;
1733 MemoryInfo
.Type
= MEM_MAPPED
;
1736 /* Find the memory area the specified address belongs to */
1737 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1738 ASSERT(MemoryArea
!= NULL
);
1740 /* Determine information dependent on the memory area type */
1741 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1743 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1744 if (!NT_SUCCESS(Status
))
1746 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1747 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1748 NT_ASSERT(NT_SUCCESS(Status
));
1753 /* Build the initial information block */
1754 Address
= PAGE_ALIGN(BaseAddress
);
1755 MemoryInfo
.BaseAddress
= Address
;
1756 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1757 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1758 MemoryInfo
.Type
= MEM_PRIVATE
;
1760 /* Acquire the working set lock (shared is enough) */
1761 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1763 /* Find the largest chunk of memory which has the same state and protection mask */
1764 MemoryInfo
.State
= MiQueryAddressState(Address
,
1767 &MemoryInfo
.Protect
,
1769 Address
= NextAddress
;
1770 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1772 /* Keep going unless the state or protection mask changed */
1773 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1774 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1775 Address
= NextAddress
;
1778 /* Release the working set lock */
1779 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1781 /* Check if we went outside of the VAD */
1782 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1784 /* Set the end of the VAD as the end address */
1785 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1788 /* Now that we know the last VA address, calculate the region size */
1789 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1792 /* Unlock the address space of the process */
1793 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1795 /* Check if we were attached */
1796 if (ProcessHandle
!= NtCurrentProcess())
1798 /* Detach and derefernece the process */
1799 KeUnstackDetachProcess(&ApcState
);
1800 ObDereferenceObject(TargetProcess
);
1803 /* Return the data, NtQueryInformation already probed it */
1804 if (PreviousMode
!= KernelMode
)
1808 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1809 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1811 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1813 Status
= _SEH2_GetExceptionCode();
1819 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1820 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1824 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1825 "State: %lx Type: %lx Size: %lx\n",
1826 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1827 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1828 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1835 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1836 IN ULONG_PTR EndingAddress
,
1838 IN PEPROCESS Process
)
1840 PMMPTE PointerPte
, LastPte
, PointerPde
;
1841 BOOLEAN OnBoundary
= TRUE
;
1844 /* Get the PDE and PTE addresses */
1845 PointerPde
= MiAddressToPde(StartingAddress
);
1846 PointerPte
= MiAddressToPte(StartingAddress
);
1847 LastPte
= MiAddressToPte(EndingAddress
);
1849 /* Loop all the PTEs */
1850 while (PointerPte
<= LastPte
)
1852 /* Check if we've hit an new PDE boundary */
1855 /* Is this PDE demand zero? */
1856 PointerPde
= MiAddressToPte(PointerPte
);
1857 if (PointerPde
->u
.Long
!= 0)
1859 /* It isn't -- is it valid? */
1860 if (PointerPde
->u
.Hard
.Valid
== 0)
1862 /* Nope, fault it in */
1863 PointerPte
= MiPteToAddress(PointerPde
);
1864 MiMakeSystemAddressValid(PointerPte
, Process
);
1869 /* The PTE was already valid, so move to the next one */
1871 PointerPte
= MiPteToAddress(PointerPde
);
1873 /* Is the entire VAD committed? If not, fail */
1874 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1876 /* Everything is committed so far past the range, return true */
1877 if (PointerPte
> LastPte
) return TRUE
;
1881 /* Is the PTE demand zero? */
1882 if (PointerPte
->u
.Long
== 0)
1884 /* Is the entire VAD committed? If not, fail */
1885 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1889 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1890 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1891 (PointerPte
->u
.Hard
.Valid
== 0) &&
1892 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1893 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1895 /* Then part of the range is decommitted, so fail */
1900 /* Move to the next PTE */
1902 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1905 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1911 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1912 IN OUT PVOID
*BaseAddress
,
1913 IN OUT PSIZE_T NumberOfBytesToProtect
,
1914 IN ULONG NewAccessProtection
,
1915 OUT PULONG OldAccessProtection OPTIONAL
)
1917 PMEMORY_AREA MemoryArea
;
1918 PMMSUPPORT AddressSpace
;
1919 ULONG OldAccessProtection_
;
1922 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1923 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1925 AddressSpace
= &Process
->Vm
;
1926 MmLockAddressSpace(AddressSpace
);
1927 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
1928 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
1930 MmUnlockAddressSpace(AddressSpace
);
1931 return STATUS_UNSUCCESSFUL
;
1934 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
1936 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
1937 Status
= MmProtectSectionView(AddressSpace
,
1940 *NumberOfBytesToProtect
,
1941 NewAccessProtection
,
1942 OldAccessProtection
);
1944 MmUnlockAddressSpace(AddressSpace
);
1951 MiProtectVirtualMemory(IN PEPROCESS Process
,
1952 IN OUT PVOID
*BaseAddress
,
1953 IN OUT PSIZE_T NumberOfBytesToProtect
,
1954 IN ULONG NewAccessProtection
,
1955 OUT PULONG OldAccessProtection OPTIONAL
)
1957 PMEMORY_AREA MemoryArea
;
1959 PMMSUPPORT AddressSpace
;
1960 ULONG_PTR StartingAddress
, EndingAddress
;
1961 PMMPTE PointerPde
, PointerPte
, LastPte
;
1964 ULONG ProtectionMask
, OldProtect
;
1966 NTSTATUS Status
= STATUS_SUCCESS
;
1967 PETHREAD Thread
= PsGetCurrentThread();
1968 TABLE_SEARCH_RESULT Result
;
1970 /* Calculate base address for the VAD */
1971 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1972 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1974 /* Calculate the protection mask and make sure it's valid */
1975 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1976 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1978 DPRINT1("Invalid protection mask\n");
1979 return STATUS_INVALID_PAGE_PROTECTION
;
1982 /* Check for ROS specific memory area */
1983 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1984 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1987 return MiRosProtectVirtualMemory(Process
,
1989 NumberOfBytesToProtect
,
1990 NewAccessProtection
,
1991 OldAccessProtection
);
1994 /* Lock the address space and make sure the process isn't already dead */
1995 AddressSpace
= MmGetCurrentAddressSpace();
1996 MmLockAddressSpace(AddressSpace
);
1997 if (Process
->VmDeleted
)
1999 DPRINT1("Process is dying\n");
2000 Status
= STATUS_PROCESS_IS_TERMINATING
;
2004 /* Get the VAD for this address range, and make sure it exists */
2005 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2006 EndingAddress
>> PAGE_SHIFT
,
2008 (PMMADDRESS_NODE
*)&Vad
);
2009 if (Result
!= TableFoundNode
)
2011 DPRINT("Could not find a VAD for this allocation\n");
2012 Status
= STATUS_CONFLICTING_ADDRESSES
;
2016 /* Make sure the address is within this VAD's boundaries */
2017 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2018 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2020 Status
= STATUS_CONFLICTING_ADDRESSES
;
2024 /* These kinds of VADs are not supported atm */
2025 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2026 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2027 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2029 DPRINT1("Illegal VAD for attempting to set protection\n");
2030 Status
= STATUS_CONFLICTING_ADDRESSES
;
2034 /* Check for a VAD whose protection can't be changed */
2035 if (Vad
->u
.VadFlags
.NoChange
== 1)
2037 DPRINT1("Trying to change protection of a NoChange VAD\n");
2038 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2042 /* Is this section, or private memory? */
2043 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2045 /* Not yet supported */
2046 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2048 DPRINT1("Illegal VAD for attempting to set protection\n");
2049 Status
= STATUS_CONFLICTING_ADDRESSES
;
2053 /* Rotate VADs are not yet supported */
2054 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2056 DPRINT1("Illegal VAD for attempting to set protection\n");
2057 Status
= STATUS_CONFLICTING_ADDRESSES
;
2061 /* Not valid on section files */
2062 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2065 DPRINT1("Invalid protection flags for section\n");
2066 Status
= STATUS_INVALID_PARAMETER_4
;
2070 /* Check if data or page file mapping protection PTE is compatible */
2071 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2074 DPRINT1("Fixme: Not checking for valid protection\n");
2077 /* This is a section, and this is not yet supported */
2078 DPRINT1("Section protection not yet supported\n");
2083 /* Private memory, check protection flags */
2084 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2085 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2087 DPRINT1("Invalid protection flags for private memory\n");
2088 Status
= STATUS_INVALID_PARAMETER_4
;
2092 /* Lock the working set */
2093 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2095 /* Check if all pages in this range are committed */
2096 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2103 DPRINT1("The entire range is not committed\n");
2104 Status
= STATUS_NOT_COMMITTED
;
2105 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2109 /* Compute starting and ending PTE and PDE addresses */
2110 PointerPde
= MiAddressToPde(StartingAddress
);
2111 PointerPte
= MiAddressToPte(StartingAddress
);
2112 LastPte
= MiAddressToPte(EndingAddress
);
2114 /* Make this PDE valid */
2115 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2117 /* Save protection of the first page */
2118 if (PointerPte
->u
.Long
!= 0)
2120 /* Capture the page protection and make the PDE valid */
2121 OldProtect
= MiGetPageProtection(PointerPte
);
2122 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2126 /* Grab the old protection from the VAD itself */
2127 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2130 /* Loop all the PTEs now */
2131 while (PointerPte
<= LastPte
)
2133 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2134 if (MiIsPteOnPdeBoundary(PointerPte
))
2136 PointerPde
= MiAddressToPte(PointerPte
);
2137 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2140 /* Capture the PTE and check if it was empty */
2141 PteContents
= *PointerPte
;
2142 if (PteContents
.u
.Long
== 0)
2144 /* This used to be a zero PTE and it no longer is, so we must add a
2145 reference to the pagetable. */
2146 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2149 /* Check what kind of PTE we are dealing with */
2150 if (PteContents
.u
.Hard
.Valid
== 1)
2152 /* Get the PFN entry */
2153 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2155 /* We don't support this yet */
2156 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2158 /* Check if the page should not be accessible at all */
2159 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2160 (NewAccessProtection
& PAGE_GUARD
))
2162 /* The page should be in the WS and we should make it transition now */
2163 DPRINT1("Making valid page invalid is not yet supported!\n");
2164 Status
= STATUS_NOT_IMPLEMENTED
;
2165 /* Unlock the working set */
2166 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2170 /* Write the protection mask and write it with a TLB flush */
2171 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2172 MiFlushTbAndCapture(Vad
,
2180 /* We don't support these cases yet */
2181 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2182 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2184 /* The PTE is already demand-zero, just update the protection mask */
2185 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2186 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2187 ASSERT(PointerPte
->u
.Long
!= 0);
2190 /* Move to the next PTE */
2194 /* Unlock the working set */
2195 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2198 /* Unlock the address space */
2199 MmUnlockAddressSpace(AddressSpace
);
2201 /* Return parameters and success */
2202 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2203 *BaseAddress
= (PVOID
)StartingAddress
;
2204 *OldAccessProtection
= OldProtect
;
2205 return STATUS_SUCCESS
;
2208 /* Unlock the address space and return the failure code */
2209 MmUnlockAddressSpace(AddressSpace
);
2215 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2216 IN PEPROCESS TargetProcess
,
2219 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2222 // Sanity checks. The latter is because we only use this function with the
2223 // PFN lock not held, so it may go away in the future.
2225 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2226 ASSERT(OldIrql
== MM_NOIRQL
);
2229 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2230 // return the same address as the PDE on 2-level page table systems.
2232 // If everything is already valid, there is nothing to do.
2234 PointerPpe
= MiAddressToPte(PointerPde
);
2235 PointerPxe
= MiAddressToPde(PointerPde
);
2236 if ((PointerPxe
->u
.Hard
.Valid
) &&
2237 (PointerPpe
->u
.Hard
.Valid
) &&
2238 (PointerPde
->u
.Hard
.Valid
))
2244 // At least something is invalid, so begin by getting the PTE for the PDE itself
2245 // and then lookup each additional level. We must do it in this precise order
2246 // because the pagfault.c code (as well as in Windows) depends that the next
2247 // level up (higher) must be valid when faulting a lower level
2249 PointerPte
= MiPteToAddress(PointerPde
);
2253 // Make sure APCs continued to be disabled
2255 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2258 // First, make the PXE valid if needed
2260 if (!PointerPxe
->u
.Hard
.Valid
)
2262 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2263 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2269 if (!PointerPpe
->u
.Hard
.Valid
)
2271 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2272 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2276 // And finally, make the PDE itself valid.
2278 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2281 // This should've worked the first time so the loop is really just for
2282 // show -- ASSERT that we're actually NOT going to be looping.
2284 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2285 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2286 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2287 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2288 !(PointerPpe
->u
.Hard
.Valid
) ||
2289 !(PointerPde
->u
.Hard
.Valid
));
2294 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2300 PFN_NUMBER PageFrameIndex
;
2304 // Acquire the PFN lock and loop all the PTEs in the list
2306 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2307 for (i
= 0; i
!= Count
; i
++)
2310 // The PTE must currently be valid
2312 TempPte
= *ValidPteList
[i
];
2313 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2316 // Get the PFN entry for the page itself, and then for its page table
2318 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2319 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2320 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2323 // Decrement the share count on the page table, and then on the page
2326 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2327 MI_SET_PFN_DELETED(Pfn1
);
2328 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2331 // Make the page decommitted
2333 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2337 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2338 // and then release the PFN lock
2341 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2346 MiDecommitPages(IN PVOID StartingAddress
,
2347 IN PMMPTE EndingPte
,
2348 IN PEPROCESS Process
,
2351 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2352 ULONG CommitReduction
= 0;
2353 PMMPTE ValidPteList
[256];
2357 PETHREAD CurrentThread
= PsGetCurrentThread();
2360 // Get the PTE and PTE for the address, and lock the working set
2361 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2362 // commited range ends so that we can do the right accounting.
2364 PointerPde
= MiAddressToPde(StartingAddress
);
2365 PointerPte
= MiAddressToPte(StartingAddress
);
2366 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2367 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2370 // Make the PDE valid, and now loop through each page's worth of data
2372 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2373 while (PointerPte
<= EndingPte
)
2376 // Check if we've crossed a PDE boundary
2378 if (MiIsPteOnPdeBoundary(PointerPte
))
2381 // Get the new PDE and flush the valid PTEs we had built up until
2382 // now. This helps reduce the amount of TLB flushing we have to do.
2383 // Note that Windows does a much better job using timestamps and
2384 // such, and does not flush the entire TLB all the time, but right
2385 // now we have bigger problems to worry about than TLB flushing.
2387 PointerPde
= MiAddressToPde(StartingAddress
);
2390 MiProcessValidPteList(ValidPteList
, PteCount
);
2395 // Make this PDE valid
2397 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2401 // Read this PTE. It might be active or still demand-zero.
2403 PteContents
= *PointerPte
;
2404 if (PteContents
.u
.Long
)
2407 // The PTE is active. It might be valid and in a working set, or
2408 // it might be a prototype PTE or paged out or even in transition.
2410 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2413 // It's already decommited, so there's nothing for us to do here
2420 // Remove it from the counters, and check if it was valid or not
2422 //Process->NumberOfPrivatePages--;
2423 if (PteContents
.u
.Hard
.Valid
)
2426 // It's valid. At this point make sure that it is not a ROS
2427 // PFN. Also, we don't support ProtoPTEs in this code path.
2429 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2430 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2431 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2434 // Flush any pending PTEs that we had not yet flushed, if our
2435 // list has gotten too big, then add this PTE to the flush list.
2437 if (PteCount
== 256)
2439 MiProcessValidPteList(ValidPteList
, PteCount
);
2442 ValidPteList
[PteCount
++] = PointerPte
;
2447 // We do not support any of these other scenarios at the moment
2449 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2450 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2451 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2454 // So the only other possibility is that it is still a demand
2455 // zero PTE, in which case we undo the accounting we did
2456 // earlier and simply make the page decommitted.
2458 //Process->NumberOfPrivatePages++;
2459 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2466 // This used to be a zero PTE and it no longer is, so we must add a
2467 // reference to the pagetable.
2469 MiIncrementPageTableReferences(StartingAddress
);
2472 // Next, we account for decommitted PTEs and make the PTE as such
2474 if (PointerPte
> CommitPte
) CommitReduction
++;
2475 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2479 // Move to the next PTE and the next address
2482 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2486 // Flush any dangling PTEs from the loop in the last page table, and then
2487 // release the working set and return the commit reduction accounting.
2489 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2490 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2491 return CommitReduction
;
2494 /* PUBLIC FUNCTIONS ***********************************************************/
2501 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2512 MmSecureVirtualMemory(IN PVOID Address
,
2516 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2525 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2527 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2530 /* SYSTEM CALLS ***************************************************************/
2534 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2535 IN PVOID BaseAddress
,
2537 IN SIZE_T NumberOfBytesToRead
,
2538 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2540 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2542 NTSTATUS Status
= STATUS_SUCCESS
;
2543 SIZE_T BytesRead
= 0;
2547 // Check if we came from user mode
2549 if (PreviousMode
!= KernelMode
)
2552 // Validate the read addresses
2554 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2555 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2556 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2557 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2560 // Don't allow to write into kernel space
2562 return STATUS_ACCESS_VIOLATION
;
2566 // Enter SEH for probe
2571 // Probe the output value
2573 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2578 // Get exception code
2580 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2586 // Don't do zero-byte transfers
2588 if (NumberOfBytesToRead
)
2591 // Reference the process
2593 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2599 if (NT_SUCCESS(Status
))
2604 Status
= MmCopyVirtualMemory(Process
,
2606 PsGetCurrentProcess(),
2608 NumberOfBytesToRead
,
2613 // Dereference the process
2615 ObDereferenceObject(Process
);
2620 // Check if the caller sent this parameter
2622 if (NumberOfBytesRead
)
2625 // Enter SEH to guard write
2630 // Return the number of bytes read
2632 *NumberOfBytesRead
= BytesRead
;
2634 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2648 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2649 IN PVOID BaseAddress
,
2651 IN SIZE_T NumberOfBytesToWrite
,
2652 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2654 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2656 NTSTATUS Status
= STATUS_SUCCESS
;
2657 SIZE_T BytesWritten
= 0;
2661 // Check if we came from user mode
2663 if (PreviousMode
!= KernelMode
)
2666 // Validate the read addresses
2668 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2669 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2670 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2671 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > 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 (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
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 (NumberOfBytesToWrite
)
2705 // Reference the process
2707 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2713 if (NT_SUCCESS(Status
))
2718 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2722 NumberOfBytesToWrite
,
2727 // Dereference the process
2729 ObDereferenceObject(Process
);
2734 // Check if the caller sent this parameter
2736 if (NumberOfBytesWritten
)
2739 // Enter SEH to guard write
2744 // Return the number of bytes written
2746 *NumberOfBytesWritten
= BytesWritten
;
2748 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2762 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2763 IN OUT PVOID
*UnsafeBaseAddress
,
2764 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2765 IN ULONG NewAccessProtection
,
2766 OUT PULONG UnsafeOldAccessProtection
)
2769 ULONG OldAccessProtection
;
2771 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2772 PVOID BaseAddress
= NULL
;
2773 SIZE_T NumberOfBytesToProtect
= 0;
2774 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2776 BOOLEAN Attached
= FALSE
;
2777 KAPC_STATE ApcState
;
2781 // Check for valid protection flags
2783 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2784 if (Protection
!= PAGE_NOACCESS
&&
2785 Protection
!= PAGE_READONLY
&&
2786 Protection
!= PAGE_READWRITE
&&
2787 Protection
!= PAGE_WRITECOPY
&&
2788 Protection
!= PAGE_EXECUTE
&&
2789 Protection
!= PAGE_EXECUTE_READ
&&
2790 Protection
!= PAGE_EXECUTE_READWRITE
&&
2791 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2796 return STATUS_INVALID_PAGE_PROTECTION
;
2800 // Check if we came from user mode
2802 if (PreviousMode
!= KernelMode
)
2805 // Enter SEH for probing
2810 // Validate all outputs
2812 ProbeForWritePointer(UnsafeBaseAddress
);
2813 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2814 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2819 BaseAddress
= *UnsafeBaseAddress
;
2820 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2822 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2825 // Get exception code
2827 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2836 BaseAddress
= *UnsafeBaseAddress
;
2837 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2841 // Catch illegal base address
2843 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2846 // Catch illegal region size
2848 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2853 return STATUS_INVALID_PARAMETER_3
;
2857 // 0 is also illegal
2859 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2862 // Get a reference to the process
2864 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2865 PROCESS_VM_OPERATION
,
2870 if (!NT_SUCCESS(Status
)) return Status
;
2873 // Check if we should attach
2875 if (CurrentProcess
!= Process
)
2880 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2885 // Do the actual work
2887 Status
= MiProtectVirtualMemory(Process
,
2889 &NumberOfBytesToProtect
,
2890 NewAccessProtection
,
2891 &OldAccessProtection
);
2896 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2899 // Release reference
2901 ObDereferenceObject(Process
);
2904 // Enter SEH to return data
2909 // Return data to user
2911 *UnsafeOldAccessProtection
= OldAccessProtection
;
2912 *UnsafeBaseAddress
= BaseAddress
;
2913 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2915 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2932 // HACK until we have proper WSLIST support
2933 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2935 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
2937 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
2949 // HACK until we have proper WSLIST support
2950 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2952 if (!Wsle
->u1
.e1
.LockedInWs
&&
2953 !Wsle
->u1
.e1
.LockedInMemory
)
2955 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
2958 if (LockType
& MAP_PROCESS
)
2959 Wsle
->u1
.e1
.LockedInWs
= 1;
2960 if (LockType
& MAP_SYSTEM
)
2961 Wsle
->u1
.e1
.LockedInMemory
= 1;
2970 // HACK until we have proper WSLIST support
2971 PMMWSLE Wsle
= &Pfn1
->Wsle
;
2973 if (LockType
& MAP_PROCESS
)
2974 Wsle
->u1
.e1
.LockedInWs
= 0;
2975 if (LockType
& MAP_SYSTEM
)
2976 Wsle
->u1
.e1
.LockedInMemory
= 0;
2978 if (!Wsle
->u1
.e1
.LockedInWs
&&
2979 !Wsle
->u1
.e1
.LockedInMemory
)
2981 MiDereferencePfnAndDropLockCount(Pfn1
);
2987 MiCheckVadsForLockOperation(
2988 _Inout_ PVOID
*BaseAddress
,
2989 _Inout_ PSIZE_T RegionSize
,
2990 _Inout_ PVOID
*EndAddress
)
2996 /* Get the base address and align the start address */
2997 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
2998 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
2999 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3001 /* First loop and check all VADs */
3002 CurrentVa
= *BaseAddress
;
3003 while (CurrentVa
< *EndAddress
)
3006 Vad
= MiLocateAddress(CurrentVa
);
3009 /// FIXME: this might be a memory area for a section view...
3010 return STATUS_ACCESS_VIOLATION
;
3013 /* Check VAD type */
3014 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3015 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3016 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3018 *EndAddress
= CurrentVa
;
3019 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3020 return STATUS_INCOMPATIBLE_FILE_MAP
;
3023 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3026 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3027 return STATUS_SUCCESS
;
3032 MiLockVirtualMemory(
3033 IN OUT PVOID
*BaseAddress
,
3034 IN OUT PSIZE_T RegionSize
,
3037 PEPROCESS CurrentProcess
;
3038 PMMSUPPORT AddressSpace
;
3039 PVOID CurrentVa
, EndAddress
;
3040 PMMPTE PointerPte
, LastPte
;
3042 #if (_MI_PAGING_LEVELS >= 3)
3045 #if (_MI_PAGING_LEVELS == 4)
3049 NTSTATUS Status
, TempStatus
;
3051 /* Lock the address space */
3052 AddressSpace
= MmGetCurrentAddressSpace();
3053 MmLockAddressSpace(AddressSpace
);
3055 /* Make sure we still have an address space */
3056 CurrentProcess
= PsGetCurrentProcess();
3057 if (CurrentProcess
->VmDeleted
)
3059 Status
= STATUS_PROCESS_IS_TERMINATING
;
3063 /* Check the VADs in the requested range */
3064 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3065 if (!NT_SUCCESS(Status
))
3070 /* Enter SEH for probing */
3073 /* Loop all pages and probe them */
3074 CurrentVa
= *BaseAddress
;
3075 while (CurrentVa
< EndAddress
)
3077 (void)(*(volatile CHAR
*)CurrentVa
);
3078 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3081 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3083 Status
= _SEH2_GetExceptionCode();
3088 /* All pages were accessible, since we hold the address space lock, nothing
3089 can be de-committed. Assume success for now. */
3090 Status
= STATUS_SUCCESS
;
3092 /* Get the PTE and PDE */
3093 PointerPte
= MiAddressToPte(*BaseAddress
);
3094 PointerPde
= MiAddressToPde(*BaseAddress
);
3095 #if (_MI_PAGING_LEVELS >= 3)
3096 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3098 #if (_MI_PAGING_LEVELS == 4)
3099 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3102 /* Get the last PTE */
3103 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3105 /* Lock the process working set */
3106 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3108 /* Loop the pages */
3111 /* Check for a page that is not accessible */
3113 #if (_MI_PAGING_LEVELS == 4)
3114 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3116 #if (_MI_PAGING_LEVELS >= 3)
3117 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3119 (PointerPde
->u
.Hard
.Valid
== 0) ||
3120 (PointerPte
->u
.Hard
.Valid
== 0))
3122 /* Release process working set */
3123 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3125 /* Access the page */
3126 CurrentVa
= MiPteToAddress(PointerPte
);
3128 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3129 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3130 if (!NT_SUCCESS(TempStatus
))
3132 // This should only happen, when remote backing storage is not accessible
3134 Status
= TempStatus
;
3138 /* Lock the process working set */
3139 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3143 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3144 ASSERT(Pfn1
!= NULL
);
3146 /* Check the previous lock status */
3147 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3149 Status
= STATUS_WAS_LOCKED
;
3153 MI_LOCK_VA(Pfn1
, MapType
);
3155 /* Go to the next PTE */
3158 /* Check if we're on a PDE boundary */
3159 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3160 #if (_MI_PAGING_LEVELS >= 3)
3161 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3163 #if (_MI_PAGING_LEVELS == 4)
3164 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3166 } while (PointerPte
<= LastPte
);
3168 /* Release process working set */
3169 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3172 /* Unlock address space */
3173 MmUnlockAddressSpace(AddressSpace
);
3180 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3181 IN OUT PVOID
*BaseAddress
,
3182 IN OUT PSIZE_T NumberOfBytesToLock
,
3186 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3188 BOOLEAN Attached
= FALSE
;
3189 KAPC_STATE ApcState
;
3190 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3191 PVOID CapturedBaseAddress
;
3192 SIZE_T CapturedBytesToLock
;
3198 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3201 // Invalid set of flags
3203 return STATUS_INVALID_PARAMETER
;
3207 // At least one flag must be specified
3209 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3214 return STATUS_INVALID_PARAMETER
;
3218 // Enter SEH for probing
3223 // Validate output data
3225 ProbeForWritePointer(BaseAddress
);
3226 ProbeForWriteSize_t(NumberOfBytesToLock
);
3231 CapturedBaseAddress
= *BaseAddress
;
3232 CapturedBytesToLock
= *NumberOfBytesToLock
;
3234 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3237 // Get exception code
3239 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3244 // Catch illegal base address
3246 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3249 // Catch illegal region size
3251 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3256 return STATUS_INVALID_PARAMETER
;
3260 // 0 is also illegal
3262 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3265 // Get a reference to the process
3267 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3268 PROCESS_VM_OPERATION
,
3273 if (!NT_SUCCESS(Status
)) return Status
;
3276 // Check if this is is system-mapped
3278 if (MapType
& MAP_SYSTEM
)
3281 // Check for required privilege
3283 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3286 // Fail: Don't have it
3288 ObDereferenceObject(Process
);
3289 return STATUS_PRIVILEGE_NOT_HELD
;
3294 // Check if we should attach
3296 if (CurrentProcess
!= Process
)
3301 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3306 // Call the internal function
3308 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3309 &CapturedBytesToLock
,
3315 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3318 // Release reference
3320 ObDereferenceObject(Process
);
3323 // Enter SEH to return data
3328 // Return data to user
3330 *BaseAddress
= CapturedBaseAddress
;
3331 *NumberOfBytesToLock
= CapturedBytesToLock
;
3333 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3336 // Get exception code
3338 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3351 MiUnlockVirtualMemory(
3352 IN OUT PVOID
*BaseAddress
,
3353 IN OUT PSIZE_T RegionSize
,
3356 PEPROCESS CurrentProcess
;
3357 PMMSUPPORT AddressSpace
;
3359 PMMPTE PointerPte
, LastPte
;
3361 #if (_MI_PAGING_LEVELS >= 3)
3364 #if (_MI_PAGING_LEVELS == 4)
3370 /* Lock the address space */
3371 AddressSpace
= MmGetCurrentAddressSpace();
3372 MmLockAddressSpace(AddressSpace
);
3374 /* Make sure we still have an address space */
3375 CurrentProcess
= PsGetCurrentProcess();
3376 if (CurrentProcess
->VmDeleted
)
3378 Status
= STATUS_PROCESS_IS_TERMINATING
;
3382 /* Check the VADs in the requested range */
3383 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3385 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3386 incompatible VAD we continue, like Windows does */
3387 if (Status
== STATUS_ACCESS_VIOLATION
)
3389 Status
= STATUS_NOT_LOCKED
;
3393 /* Get the PTE and PDE */
3394 PointerPte
= MiAddressToPte(*BaseAddress
);
3395 PointerPde
= MiAddressToPde(*BaseAddress
);
3396 #if (_MI_PAGING_LEVELS >= 3)
3397 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3399 #if (_MI_PAGING_LEVELS == 4)
3400 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3403 /* Get the last PTE */
3404 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3406 /* Lock the process working set */
3407 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3409 /* Loop the pages */
3412 /* Check for a page that is not present */
3414 #if (_MI_PAGING_LEVELS == 4)
3415 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3417 #if (_MI_PAGING_LEVELS >= 3)
3418 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3420 (PointerPde
->u
.Hard
.Valid
== 0) ||
3421 (PointerPte
->u
.Hard
.Valid
== 0))
3423 /* Remember it, but keep going */
3424 Status
= STATUS_NOT_LOCKED
;
3429 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3430 ASSERT(Pfn1
!= NULL
);
3432 /* Check if all of the requested locks are present */
3433 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3434 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3436 /* Remember it, but keep going */
3437 Status
= STATUS_NOT_LOCKED
;
3439 /* Check if no lock is present */
3440 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3442 DPRINT1("FIXME: Should remove the page from WS\n");
3447 /* Go to the next PTE */
3450 /* Check if we're on a PDE boundary */
3451 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3452 #if (_MI_PAGING_LEVELS >= 3)
3453 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3455 #if (_MI_PAGING_LEVELS == 4)
3456 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3458 } while (PointerPte
<= LastPte
);
3460 /* Check if we hit a page that was not locked */
3461 if (Status
== STATUS_NOT_LOCKED
)
3463 goto CleanupWithWsLock
;
3466 /* All pages in the region were locked, so unlock them all */
3468 /* Get the PTE and PDE */
3469 PointerPte
= MiAddressToPte(*BaseAddress
);
3470 PointerPde
= MiAddressToPde(*BaseAddress
);
3471 #if (_MI_PAGING_LEVELS >= 3)
3472 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3474 #if (_MI_PAGING_LEVELS == 4)
3475 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3478 /* Loop the pages */
3482 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3483 MI_UNLOCK_VA(Pfn1
, MapType
);
3485 /* Go to the next PTE */
3488 /* Check if we're on a PDE boundary */
3489 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3490 #if (_MI_PAGING_LEVELS >= 3)
3491 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3493 #if (_MI_PAGING_LEVELS == 4)
3494 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3496 } while (PointerPte
<= LastPte
);
3498 /* Everything is done */
3499 Status
= STATUS_SUCCESS
;
3503 /* Release process working set */
3504 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3507 /* Unlock address space */
3508 MmUnlockAddressSpace(AddressSpace
);
3516 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3517 IN OUT PVOID
*BaseAddress
,
3518 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3522 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3524 BOOLEAN Attached
= FALSE
;
3525 KAPC_STATE ApcState
;
3526 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3527 PVOID CapturedBaseAddress
;
3528 SIZE_T CapturedBytesToUnlock
;
3534 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3537 // Invalid set of flags
3539 return STATUS_INVALID_PARAMETER
;
3543 // At least one flag must be specified
3545 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3550 return STATUS_INVALID_PARAMETER
;
3554 // Enter SEH for probing
3559 // Validate output data
3561 ProbeForWritePointer(BaseAddress
);
3562 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3567 CapturedBaseAddress
= *BaseAddress
;
3568 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3573 // Get exception code
3575 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3580 // Catch illegal base address
3582 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3585 // Catch illegal region size
3587 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3592 return STATUS_INVALID_PARAMETER
;
3596 // 0 is also illegal
3598 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3601 // Get a reference to the process
3603 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3604 PROCESS_VM_OPERATION
,
3609 if (!NT_SUCCESS(Status
)) return Status
;
3612 // Check if this is is system-mapped
3614 if (MapType
& MAP_SYSTEM
)
3617 // Check for required privilege
3619 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3622 // Fail: Don't have it
3624 ObDereferenceObject(Process
);
3625 return STATUS_PRIVILEGE_NOT_HELD
;
3630 // Check if we should attach
3632 if (CurrentProcess
!= Process
)
3637 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3642 // Call the internal function
3644 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3645 &CapturedBytesToUnlock
,
3651 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3654 // Release reference
3656 ObDereferenceObject(Process
);
3659 // Enter SEH to return data
3664 // Return data to user
3666 *BaseAddress
= CapturedBaseAddress
;
3667 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3669 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3672 // Get exception code
3674 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3681 return STATUS_SUCCESS
;
3686 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3687 IN OUT PVOID
*BaseAddress
,
3688 IN OUT PSIZE_T NumberOfBytesToFlush
,
3689 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3693 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3694 PVOID CapturedBaseAddress
;
3695 SIZE_T CapturedBytesToFlush
;
3696 IO_STATUS_BLOCK LocalStatusBlock
;
3700 // Check if we came from user mode
3702 if (PreviousMode
!= KernelMode
)
3705 // Enter SEH for probing
3710 // Validate all outputs
3712 ProbeForWritePointer(BaseAddress
);
3713 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3714 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3719 CapturedBaseAddress
= *BaseAddress
;
3720 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3722 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3725 // Get exception code
3727 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3736 CapturedBaseAddress
= *BaseAddress
;
3737 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3741 // Catch illegal base address
3743 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3746 // Catch illegal region size
3748 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3753 return STATUS_INVALID_PARAMETER
;
3757 // Get a reference to the process
3759 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3760 PROCESS_VM_OPERATION
,
3765 if (!NT_SUCCESS(Status
)) return Status
;
3770 Status
= MmFlushVirtualMemory(Process
,
3771 &CapturedBaseAddress
,
3772 &CapturedBytesToFlush
,
3776 // Release reference
3778 ObDereferenceObject(Process
);
3781 // Enter SEH to return data
3786 // Return data to user
3788 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3789 *NumberOfBytesToFlush
= 0;
3790 *IoStatusBlock
= LocalStatusBlock
;
3792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3808 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3810 IN PVOID BaseAddress
,
3811 IN SIZE_T RegionSize
,
3812 IN PVOID
*UserAddressArray
,
3813 OUT PULONG_PTR EntriesInUserAddressArray
,
3814 OUT PULONG Granularity
)
3819 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3820 ULONG_PTR CapturedEntryCount
;
3824 // Check if we came from user mode
3826 if (PreviousMode
!= KernelMode
)
3829 // Enter SEH for probing
3834 // Catch illegal base address
3836 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3839 // Catch illegal region size
3841 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3846 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
3850 // Validate all data
3852 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3853 ProbeForWriteUlong(Granularity
);
3858 CapturedEntryCount
= *EntriesInUserAddressArray
;
3861 // Must have a count
3863 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3866 // Can't be larger than the maximum
3868 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3873 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3877 // Probe the actual array
3879 ProbeForWrite(UserAddressArray
,
3880 CapturedEntryCount
* sizeof(PVOID
),
3883 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3886 // Get exception code
3888 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3897 CapturedEntryCount
= *EntriesInUserAddressArray
;
3898 ASSERT(CapturedEntryCount
!= 0);
3902 // Check if this is a local request
3904 if (ProcessHandle
== NtCurrentProcess())
3907 // No need to reference the process
3909 Process
= PsGetCurrentProcess();
3914 // Reference the target
3916 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3917 PROCESS_VM_OPERATION
,
3922 if (!NT_SUCCESS(Status
)) return Status
;
3926 // Compute the last address and validate it
3928 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3929 if (BaseAddress
> EndAddress
)
3934 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3935 return STATUS_INVALID_PARAMETER_4
;
3944 // Dereference if needed
3946 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3949 // Enter SEH to return data
3954 // Return data to user
3956 *EntriesInUserAddressArray
= 0;
3957 *Granularity
= PAGE_SIZE
;
3959 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3962 // Get exception code
3964 Status
= _SEH2_GetExceptionCode();
3971 return STATUS_SUCCESS
;
3979 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3980 IN PVOID BaseAddress
,
3981 IN SIZE_T RegionSize
)
3986 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3987 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3990 // Catch illegal base address
3992 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3995 // Catch illegal region size
3997 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4002 return STATUS_INVALID_PARAMETER_3
;
4006 // Check if this is a local request
4008 if (ProcessHandle
== NtCurrentProcess())
4011 // No need to reference the process
4013 Process
= PsGetCurrentProcess();
4018 // Reference the target
4020 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4021 PROCESS_VM_OPERATION
,
4026 if (!NT_SUCCESS(Status
)) return Status
;
4030 // Compute the last address and validate it
4032 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4033 if (BaseAddress
> EndAddress
)
4038 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4039 return STATUS_INVALID_PARAMETER_3
;
4048 // Dereference if needed
4050 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4055 return STATUS_SUCCESS
;
4060 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4061 IN PVOID BaseAddress
,
4062 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4063 OUT PVOID MemoryInformation
,
4064 IN SIZE_T MemoryInformationLength
,
4065 OUT PSIZE_T ReturnLength
)
4067 NTSTATUS Status
= STATUS_SUCCESS
;
4068 KPROCESSOR_MODE PreviousMode
;
4070 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4072 /* Bail out if the address is invalid */
4073 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4075 /* Probe return buffer */
4076 PreviousMode
= ExGetPreviousMode();
4077 if (PreviousMode
!= KernelMode
)
4081 ProbeForWrite(MemoryInformation
,
4082 MemoryInformationLength
,
4085 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4087 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4089 Status
= _SEH2_GetExceptionCode();
4093 if (!NT_SUCCESS(Status
))
4099 switch(MemoryInformationClass
)
4101 case MemoryBasicInformation
:
4102 /* Validate the size information of the class */
4103 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4105 /* The size is invalid */
4106 return STATUS_INFO_LENGTH_MISMATCH
;
4108 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4111 MemoryInformationLength
,
4115 case MemorySectionName
:
4116 /* Validate the size information of the class */
4117 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4119 /* The size is invalid */
4120 return STATUS_INFO_LENGTH_MISMATCH
;
4122 Status
= MiQueryMemorySectionName(ProcessHandle
,
4125 MemoryInformationLength
,
4128 case MemoryWorkingSetList
:
4129 case MemoryBasicVlmInformation
:
4131 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4143 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4144 IN OUT PVOID
* UBaseAddress
,
4145 IN ULONG_PTR ZeroBits
,
4146 IN OUT PSIZE_T URegionSize
,
4147 IN ULONG AllocationType
,
4151 PMEMORY_AREA MemoryArea
;
4152 PFN_NUMBER PageCount
;
4153 PMMVAD Vad
= NULL
, FoundVad
;
4155 PMMSUPPORT AddressSpace
;
4157 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
, HighestAddress
;
4158 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4159 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4160 PETHREAD CurrentThread
= PsGetCurrentThread();
4161 KAPC_STATE ApcState
;
4162 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4163 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4165 PMMPTE PointerPte
, PointerPde
, LastPte
;
4166 TABLE_SEARCH_RESULT Result
;
4167 PMMADDRESS_NODE Parent
;
4170 /* Check for valid Zero bits */
4171 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4173 DPRINT1("Too many zero bits\n");
4174 return STATUS_INVALID_PARAMETER_3
;
4177 /* Check for valid Allocation Types */
4178 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4179 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4181 DPRINT1("Invalid Allocation Type\n");
4182 return STATUS_INVALID_PARAMETER_5
;
4185 /* Check for at least one of these Allocation Types to be set */
4186 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4188 DPRINT1("No memory allocation base type\n");
4189 return STATUS_INVALID_PARAMETER_5
;
4192 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4193 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4195 DPRINT1("Invalid use of MEM_RESET\n");
4196 return STATUS_INVALID_PARAMETER_5
;
4199 /* Check if large pages are being used */
4200 if (AllocationType
& MEM_LARGE_PAGES
)
4202 /* Large page allocations MUST be committed */
4203 if (!(AllocationType
& MEM_COMMIT
))
4205 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4206 return STATUS_INVALID_PARAMETER_5
;
4209 /* These flags are not allowed with large page allocations */
4210 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4212 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4213 return STATUS_INVALID_PARAMETER_5
;
4217 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4218 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4220 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4221 return STATUS_INVALID_PARAMETER_5
;
4224 /* Check for valid MEM_PHYSICAL usage */
4225 if (AllocationType
& MEM_PHYSICAL
)
4227 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4228 if (!(AllocationType
& MEM_RESERVE
))
4230 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4231 return STATUS_INVALID_PARAMETER_5
;
4234 /* Only these flags are allowed with MEM_PHYSIAL */
4235 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4237 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4238 return STATUS_INVALID_PARAMETER_5
;
4241 /* Then make sure PAGE_READWRITE is used */
4242 if (Protect
!= PAGE_READWRITE
)
4244 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4245 return STATUS_INVALID_PARAMETER_6
;
4249 /* Calculate the protection mask and make sure it's valid */
4250 ProtectionMask
= MiMakeProtectionMask(Protect
);
4251 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4253 DPRINT1("Invalid protection mask\n");
4254 return STATUS_INVALID_PAGE_PROTECTION
;
4260 /* Check for user-mode parameters */
4261 if (PreviousMode
!= KernelMode
)
4263 /* Make sure they are writable */
4264 ProbeForWritePointer(UBaseAddress
);
4265 ProbeForWriteSize_t(URegionSize
);
4268 /* Capture their values */
4269 PBaseAddress
= *UBaseAddress
;
4270 PRegionSize
= *URegionSize
;
4272 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4274 /* Return the exception code */
4275 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4279 /* Make sure the allocation isn't past the VAD area */
4280 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4282 DPRINT1("Virtual allocation base above User Space\n");
4283 return STATUS_INVALID_PARAMETER_2
;
4286 /* Make sure the allocation wouldn't overflow past the VAD area */
4287 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4289 DPRINT1("Region size would overflow into kernel-memory\n");
4290 return STATUS_INVALID_PARAMETER_4
;
4293 /* Make sure there's a size specified */
4296 DPRINT1("Region size is invalid (zero)\n");
4297 return STATUS_INVALID_PARAMETER_4
;
4301 // If this is for the current process, just use PsGetCurrentProcess
4303 if (ProcessHandle
== NtCurrentProcess())
4305 Process
= CurrentProcess
;
4310 // Otherwise, reference the process with VM rights and attach to it if
4311 // this isn't the current process. We must attach because we'll be touching
4312 // PTEs and PDEs that belong to user-mode memory, and also touching the
4313 // Working Set which is stored in Hyperspace.
4315 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4316 PROCESS_VM_OPERATION
,
4321 if (!NT_SUCCESS(Status
)) return Status
;
4322 if (CurrentProcess
!= Process
)
4324 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4330 // Check for large page allocations and make sure that the required privilege
4331 // is being held, before attempting to handle them.
4333 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4334 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4336 /* Fail without it */
4337 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4338 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4339 goto FailPathNoLock
;
4343 // Fail on the things we don't yet support
4345 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4347 DPRINT1("MEM_LARGE_PAGES not supported\n");
4348 Status
= STATUS_INVALID_PARAMETER
;
4349 goto FailPathNoLock
;
4351 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4353 DPRINT1("MEM_PHYSICAL not supported\n");
4354 Status
= STATUS_INVALID_PARAMETER
;
4355 goto FailPathNoLock
;
4357 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4359 DPRINT1("MEM_WRITE_WATCH not supported\n");
4360 Status
= STATUS_INVALID_PARAMETER
;
4361 goto FailPathNoLock
;
4365 // Check if the caller is reserving memory, or committing memory and letting
4366 // us pick the base address
4368 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4371 // Do not allow COPY_ON_WRITE through this API
4373 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4375 DPRINT1("Copy on write not allowed through this path\n");
4376 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4377 goto FailPathNoLock
;
4381 // Does the caller have an address in mind, or is this a blind commit?
4386 // This is a blind commit, all we need is the region size
4388 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4389 PageCount
= BYTES_TO_PAGES(PRegionSize
);
4391 StartingAddress
= 0;
4394 // Check if ZeroBits were specified
4399 // Calculate the highest address and check if it's valid
4401 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4402 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4404 Status
= STATUS_INVALID_PARAMETER_3
;
4405 goto FailPathNoLock
;
4411 // Use the highest VAD address as maximum
4413 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4419 // This is a reservation, so compute the starting address on the
4420 // expected 64KB granularity, and see where the ending address will
4421 // fall based on the aligned address and the passed in region size
4423 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4424 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4425 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
4429 // Allocate and initialize the VAD
4431 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4434 DPRINT1("Failed to allocate a VAD!\n");
4435 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4436 goto FailPathNoLock
;
4439 Vad
->u
.LongFlags
= 0;
4440 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4441 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4442 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4443 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
4446 // Lock the address space and make sure the process isn't already dead
4448 AddressSpace
= MmGetCurrentAddressSpace();
4449 MmLockAddressSpace(AddressSpace
);
4450 if (Process
->VmDeleted
)
4452 Status
= STATUS_PROCESS_IS_TERMINATING
;
4457 // Did we have a base address? If no, find a valid address that is 64KB
4458 // aligned in the VAD tree. Otherwise, make sure that the address range
4459 // which was passed in isn't already conflicting with an existing address
4464 /* Which way should we search? */
4465 if ((AllocationType
& MEM_TOP_DOWN
) || Process
->VmTopDown
)
4467 /* Find an address top-down */
4468 Result
= MiFindEmptyAddressRangeDownTree(PRegionSize
,
4477 /* Find an address bottom-up */
4478 Result
= MiFindEmptyAddressRangeInTree(PRegionSize
,
4485 if (Result
== TableFoundNode
)
4487 Status
= STATUS_NO_MEMORY
;
4492 // Now we know where the allocation ends. Make sure it doesn't end up
4493 // somewhere in kernel mode.
4495 ASSERT(StartingAddress
!= 0);
4496 ASSERT(StartingAddress
< (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
);
4497 EndingAddress
= (StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4498 ASSERT(EndingAddress
> StartingAddress
);
4499 if (EndingAddress
> HighestAddress
)
4501 Status
= STATUS_NO_MEMORY
;
4507 /* Make sure it doesn't conflict with an existing allocation */
4508 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4509 EndingAddress
>> PAGE_SHIFT
,
4512 if (Result
== TableFoundNode
)
4515 // The address specified is in conflict!
4517 Status
= STATUS_CONFLICTING_ADDRESSES
;
4523 // Write out the VAD fields for this allocation
4525 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
4526 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
4529 // FIXME: Should setup VAD bitmap
4531 Status
= STATUS_SUCCESS
;
4534 // Lock the working set and insert the VAD into the process VAD tree
4536 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4537 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4538 Process
->VadRoot
.NodeHint
= Vad
;
4539 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
4540 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4543 // Make sure the actual region size is at least as big as the
4544 // requested region size and update the value
4546 ASSERT(PRegionSize
<= (EndingAddress
+ 1 - StartingAddress
));
4547 PRegionSize
= (EndingAddress
+ 1 - StartingAddress
);
4550 // Update the virtual size of the process, and if this is now the highest
4551 // virtual size we have ever seen, update the peak virtual size to reflect
4554 Process
->VirtualSize
+= PRegionSize
;
4555 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4557 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4561 // Release address space and detach and dereference the target process if
4562 // it was different from the current process
4564 MmUnlockAddressSpace(AddressSpace
);
4565 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4566 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4569 // Use SEH to write back the base address and the region size. In the case
4570 // of an exception, we do not return back the exception code, as the memory
4571 // *has* been allocated. The caller would now have to call VirtualQuery
4572 // or do some other similar trick to actually find out where its memory
4573 // allocation ended up
4577 *URegionSize
= PRegionSize
;
4578 *UBaseAddress
= (PVOID
)StartingAddress
;
4580 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4583 // Ignore exception!
4587 return STATUS_SUCCESS
;
4591 // This is a MEM_COMMIT on top of an existing address which must have been
4592 // MEM_RESERVED already. Compute the start and ending base addresses based
4593 // on the user input, and then compute the actual region size once all the
4594 // alignments have been done.
4596 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4597 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4598 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4601 // Lock the address space and make sure the process isn't already dead
4603 AddressSpace
= MmGetCurrentAddressSpace();
4604 MmLockAddressSpace(AddressSpace
);
4605 if (Process
->VmDeleted
)
4607 DPRINT1("Process is dying\n");
4608 Status
= STATUS_PROCESS_IS_TERMINATING
;
4613 // Get the VAD for this address range, and make sure it exists
4615 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4616 EndingAddress
>> PAGE_SHIFT
,
4618 (PMMADDRESS_NODE
*)&FoundVad
);
4619 if (Result
!= TableFoundNode
)
4621 DPRINT1("Could not find a VAD for this allocation\n");
4622 Status
= STATUS_CONFLICTING_ADDRESSES
;
4626 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4628 /// @todo HACK: pretend success
4629 DPRINT("MEM_RESET not supported\n");
4630 Status
= STATUS_SUCCESS
;
4635 // These kinds of VADs are illegal for this Windows function when trying to
4636 // commit an existing range
4638 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4639 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4640 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4642 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4643 Status
= STATUS_CONFLICTING_ADDRESSES
;
4648 // Make sure that this address range actually fits within the VAD for it
4650 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4651 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4653 DPRINT1("Address range does not fit into the VAD\n");
4654 Status
= STATUS_CONFLICTING_ADDRESSES
;
4659 // Make sure this is an ARM3 section
4661 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4662 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4664 DPRINT1("Illegal commit of non-ARM3 section!\n");
4665 Status
= STATUS_ALREADY_COMMITTED
;
4669 // Is this a previously reserved section being committed? If so, enter the
4670 // special section path
4672 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4675 // You cannot commit large page sections through this API
4677 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4679 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4680 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4685 // You can only use caching flags on a rotate VAD
4687 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4688 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4690 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4691 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4696 // We should make sure that the section's permissions aren't being
4699 if (FoundVad
->u
.VadFlags
.NoChange
)
4702 // Make sure it's okay to touch it
4703 // Note: The Windows 2003 kernel has a bug here, passing the
4704 // unaligned base address together with the aligned size,
4705 // potentially covering a region larger than the actual allocation.
4706 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4707 // For now we keep this behavior.
4708 // TODO: analyze possible implications, create test case
4710 Status
= MiCheckSecuredVad(FoundVad
,
4714 if (!NT_SUCCESS(Status
))
4716 DPRINT1("Secured VAD being messed around with\n");
4722 // ARM3 does not support file-backed sections, only shared memory
4724 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4727 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4729 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4730 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4732 DPRINT1("Invalid page protection for rotate VAD\n");
4733 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4738 // Compute PTE addresses and the quota charge, then grab the commit lock
4740 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4741 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4742 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4743 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4746 // Get the segment template PTE and start looping each page
4748 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4749 ASSERT(TempPte
.u
.Long
!= 0);
4750 while (PointerPte
<= LastPte
)
4753 // For each non-already-committed page, write the invalid template PTE
4755 if (PointerPte
->u
.Long
== 0)
4757 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4767 // Now do the commit accounting and release the lock
4769 ASSERT(QuotaCharge
>= QuotaFree
);
4770 QuotaCharge
-= QuotaFree
;
4771 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4772 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4775 // We are done with committing the section pages
4777 Status
= STATUS_SUCCESS
;
4782 // This is a specific ReactOS check because we only use normal VADs
4784 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4787 // While this is an actual Windows check
4789 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4792 // Throw out attempts to use copy-on-write through this API path
4794 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4796 DPRINT1("Write copy attempted when not allowed\n");
4797 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4802 // Initialize a demand-zero PTE
4805 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4806 NT_ASSERT(TempPte
.u
.Long
!= 0);
4809 // Get the PTE, PDE and the last PTE for this address range
4811 PointerPde
= MiAddressToPde(StartingAddress
);
4812 PointerPte
= MiAddressToPte(StartingAddress
);
4813 LastPte
= MiAddressToPte(EndingAddress
);
4816 // Update the commit charge in the VAD as well as in the process, and check
4817 // if this commit charge was now higher than the last recorded peak, in which
4818 // case we also update the peak
4820 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4821 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4822 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4824 Process
->CommitChargePeak
= Process
->CommitCharge
;
4828 // Lock the working set while we play with user pages and page tables
4830 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4833 // Make the current page table valid, and then loop each page within it
4835 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4836 while (PointerPte
<= LastPte
)
4839 // Have we crossed into a new page table?
4841 if (MiIsPteOnPdeBoundary(PointerPte
))
4844 // Get the PDE and now make it valid too
4846 PointerPde
= MiAddressToPte(PointerPte
);
4847 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4851 // Is this a zero PTE as expected?
4853 if (PointerPte
->u
.Long
== 0)
4856 // First increment the count of pages in the page table for this
4859 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4862 // And now write the invalid demand-zero PTE as requested
4864 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4866 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4869 // If the PTE was already decommitted, there is nothing else to do
4870 // but to write the new demand-zero PTE
4872 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4874 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4877 // We don't handle these scenarios yet
4879 if (PointerPte
->u
.Soft
.Valid
== 0)
4881 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4882 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4886 // There's a change in protection, remember this for later, but do
4887 // not yet handle it.
4889 ChangeProtection
= TRUE
;
4893 // Move to the next PTE
4899 // Release the working set lock, unlock the address space, and detach from
4900 // the target process if it was not the current process. Also dereference the
4901 // target process if this wasn't the case.
4903 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4904 Status
= STATUS_SUCCESS
;
4906 MmUnlockAddressSpace(AddressSpace
);
4908 if (!NT_SUCCESS(Status
))
4912 ExFreePoolWithTag(Vad
, 'SdaV');
4917 // Check if we need to update the protection
4919 if (ChangeProtection
)
4921 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
4922 SIZE_T ProtectSize
= PRegionSize
;
4923 ULONG OldProtection
;
4926 // Change the protection of the region
4928 MiProtectVirtualMemory(Process
,
4929 &ProtectBaseAddress
,
4936 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4937 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4940 // Only write back results on success
4942 if (NT_SUCCESS(Status
))
4945 // Use SEH to write back the base address and the region size. In the case
4946 // of an exception, we strangely do return back the exception code, even
4947 // though the memory *has* been allocated. This mimics Windows behavior and
4948 // there is not much we can do about it.
4952 *URegionSize
= PRegionSize
;
4953 *UBaseAddress
= (PVOID
)StartingAddress
;
4955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4957 Status
= _SEH2_GetExceptionCode();
4970 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4971 IN PVOID
* UBaseAddress
,
4972 IN PSIZE_T URegionSize
,
4975 PMEMORY_AREA MemoryArea
;
4978 LONG_PTR CommitReduction
= 0;
4979 ULONG_PTR StartingAddress
, EndingAddress
;
4983 PMMSUPPORT AddressSpace
;
4984 PETHREAD CurrentThread
= PsGetCurrentThread();
4985 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4986 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4987 KAPC_STATE ApcState
;
4988 BOOLEAN Attached
= FALSE
;
4992 // Only two flags are supported
4994 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4996 DPRINT1("Invalid FreeType\n");
4997 return STATUS_INVALID_PARAMETER_4
;
5001 // Check if no flag was used, or if both flags were used
5003 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5004 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5006 DPRINT1("Invalid FreeType combination\n");
5007 return STATUS_INVALID_PARAMETER_4
;
5011 // Enter SEH for probe and capture. On failure, return back to the caller
5012 // with an exception violation.
5017 // Check for user-mode parameters and make sure that they are writeable
5019 if (PreviousMode
!= KernelMode
)
5021 ProbeForWritePointer(UBaseAddress
);
5022 ProbeForWriteUlong(URegionSize
);
5026 // Capture the current values
5028 PBaseAddress
= *UBaseAddress
;
5029 PRegionSize
= *URegionSize
;
5031 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5033 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5038 // Make sure the allocation isn't past the user area
5040 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5042 DPRINT1("Virtual free base above User Space\n");
5043 return STATUS_INVALID_PARAMETER_2
;
5047 // Make sure the allocation wouldn't overflow past the user area
5049 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5051 DPRINT1("Region size would overflow into kernel-memory\n");
5052 return STATUS_INVALID_PARAMETER_3
;
5056 // If this is for the current process, just use PsGetCurrentProcess
5058 if (ProcessHandle
== NtCurrentProcess())
5060 Process
= CurrentProcess
;
5065 // Otherwise, reference the process with VM rights and attach to it if
5066 // this isn't the current process. We must attach because we'll be touching
5067 // PTEs and PDEs that belong to user-mode memory, and also touching the
5068 // Working Set which is stored in Hyperspace.
5070 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5071 PROCESS_VM_OPERATION
,
5076 if (!NT_SUCCESS(Status
)) return Status
;
5077 if (CurrentProcess
!= Process
)
5079 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5085 // Lock the address space
5087 AddressSpace
= MmGetCurrentAddressSpace();
5088 MmLockAddressSpace(AddressSpace
);
5091 // If the address space is being deleted, fail the de-allocation since it's
5092 // too late to do anything about it
5094 if (Process
->VmDeleted
)
5096 DPRINT1("Process is dead\n");
5097 Status
= STATUS_PROCESS_IS_TERMINATING
;
5102 // Compute start and end addresses, and locate the VAD
5104 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5105 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5106 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5109 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5110 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5115 // If the range exceeds the VAD's ending VPN, fail this request
5117 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5119 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5120 Status
= STATUS_UNABLE_TO_FREE_VM
;
5125 // Only private memory (except rotate VADs) can be freed through here */
5127 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5128 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5129 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5131 DPRINT1("Attempt to free section memory\n");
5132 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5137 // ARM3 does not yet handle protected VM
5139 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5142 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5143 // and that is is an ARM3 memory area, and not a section view, as we currently
5144 // don't support freeing those though this interface.
5146 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5148 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5151 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5153 if (FreeType
& MEM_RELEASE
)
5156 // ARM3 only supports this VAD in this path
5158 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5161 // Is the caller trying to remove the whole VAD, or remove only a portion
5162 // of it? If no region size is specified, then the assumption is that the
5163 // whole VAD is to be destroyed
5168 // The caller must specify the base address identically to the range
5169 // that is stored in the VAD.
5171 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5173 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5174 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5179 // Now compute the actual start/end addresses based on the VAD
5181 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5182 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5185 // Finally lock the working set and remove the VAD from the VAD tree
5187 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5188 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5189 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5194 // This means the caller wants to release a specific region within
5195 // the range. We have to find out which range this is -- the following
5196 // possibilities exist plus their union (CASE D):
5198 // STARTING ADDRESS ENDING ADDRESS
5199 // [<========][========================================][=========>]
5200 // CASE A CASE B CASE C
5203 // First, check for case A or D
5205 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5210 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5213 // This is the easiest one to handle -- it is identical to
5214 // the code path above when the caller sets a zero region size
5215 // and the whole VAD is destroyed
5217 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5218 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5219 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5224 // This case is pretty easy too -- we compute a bunch of
5225 // pages to decommit, and then push the VAD's starting address
5226 // a bit further down, then decrement the commit charge
5228 // NOT YET IMPLEMENTED IN ARM3.
5230 DPRINT1("Case A not handled\n");
5231 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5235 // After analyzing the VAD, set it to NULL so that we don't
5236 // free it in the exit path
5244 // This is case B or case C. First check for case C
5246 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5248 PMEMORY_AREA MemoryArea
;
5251 // This is pretty easy and similar to case A. We compute the
5252 // amount of pages to decommit, update the VAD's commit charge
5253 // and then change the ending address of the VAD to be a bit
5256 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5257 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5261 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5262 // For ReactOS: shrink the corresponding memory area
5263 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5264 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
5265 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
5266 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
5267 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
5272 // This is case B and the hardest one. Because we are removing
5273 // a chunk of memory from the very middle of the VAD, we must
5274 // actually split the VAD into two new VADs and compute the
5275 // commit charges for each of them, and reinsert new charges.
5277 // NOT YET IMPLEMENTED IN ARM3.
5279 DPRINT1("Case B not handled\n");
5280 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5285 // After analyzing the VAD, set it to NULL so that we don't
5286 // free it in the exit path
5293 // Now we have a range of pages to dereference, so call the right API
5294 // to do that and then release the working set, since we're done messing
5295 // around with process pages.
5297 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5298 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5299 Status
= STATUS_SUCCESS
;
5303 // Update the process counters
5305 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5306 Process
->CommitCharge
-= CommitReduction
;
5307 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5310 // Unlock the address space and free the VAD in failure cases. Next,
5311 // detach from the target process so we can write the region size and the
5312 // base address to the correct source process, and dereference the target
5315 MmUnlockAddressSpace(AddressSpace
);
5316 if (Vad
) ExFreePool(Vad
);
5317 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5318 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5321 // Use SEH to safely return the region size and the base address of the
5322 // deallocation. If we get an access violation, don't return a failure code
5323 // as the deallocation *has* happened. The caller will just have to figure
5324 // out another way to find out where it is (such as VirtualQuery).
5328 *URegionSize
= PRegionSize
;
5329 *UBaseAddress
= (PVOID
)StartingAddress
;
5331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5339 // This is the decommit path. You cannot decommit from the following VADs in
5340 // Windows, so fail the vall
5342 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5343 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5344 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5346 DPRINT1("Trying to decommit from invalid VAD\n");
5347 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5352 // If the caller did not specify a region size, first make sure that this
5353 // region is actually committed. If it is, then compute the ending address
5354 // based on the VAD.
5358 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5360 DPRINT1("Decomitting non-committed memory\n");
5361 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5364 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5368 // Decommit the PTEs for the range plus the actual backing pages for the
5369 // range, then reduce that amount from the commit charge in the VAD
5371 CommitReduction
= MiAddressToPte(EndingAddress
) -
5372 MiAddressToPte(StartingAddress
) +
5374 MiDecommitPages((PVOID
)StartingAddress
,
5375 MiAddressToPte(EndingAddress
),
5378 ASSERT(CommitReduction
>= 0);
5379 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5380 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5383 // We are done, go to the exit path without freeing the VAD as it remains
5384 // valid since we have not released the allocation.
5387 Status
= STATUS_SUCCESS
;
5391 // In the failure path, we detach and derefernece the target process, and
5392 // return whatever failure code was sent.
5395 MmUnlockAddressSpace(AddressSpace
);
5396 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5397 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5404 MmGetPhysicalAddress(PVOID Address
)
5406 PHYSICAL_ADDRESS PhysicalAddress
;
5410 /* Check if the PXE/PPE/PDE is valid */
5412 #if (_MI_PAGING_LEVELS == 4)
5413 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5415 #if (_MI_PAGING_LEVELS >= 3)
5416 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5418 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5420 /* Check for large pages */
5421 TempPde
= *MiAddressToPde(Address
);
5422 if (TempPde
.u
.Hard
.LargePage
)
5424 /* Physical address is base page + large page offset */
5425 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5426 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5427 return PhysicalAddress
;
5430 /* Check if the PTE is valid */
5431 TempPte
= *MiAddressToPte(Address
);
5432 if (TempPte
.u
.Hard
.Valid
)
5434 /* Physical address is base page + page offset */
5435 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5436 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5437 return PhysicalAddress
;
5441 KeRosDumpStackFrames(NULL
, 20);
5442 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5443 PhysicalAddress
.QuadPart
= 0;
5444 return PhysicalAddress
;