2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
10 /* So long, and Thanks for All the Fish */
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
32 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
34 IN ULONG ProtectionMask
,
36 IN BOOLEAN CaptureDirtyBit
);
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
44 IN ULONG_PTR EndingAddress
,
48 PMMPTE PointerPte
, LastPte
, PointerPde
;
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde
= MiAddressToPde(StartingAddress
);
53 PointerPte
= MiAddressToPte(StartingAddress
);
54 LastPte
= MiAddressToPte(EndingAddress
);
56 /* Handle commited pages first */
57 if (Vad
->u
.VadFlags
.MemCommit
== 1)
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages
= (ULONG
)BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
62 /* Is the PDE demand-zero? */
63 PointerPde
= MiAddressToPte(PointerPte
);
64 if (PointerPde
->u
.Long
!= 0)
66 /* It is not. Is it valid? */
67 if (PointerPde
->u
.Hard
.Valid
== 0)
70 PointerPte
= MiPteToAddress(PointerPde
);
71 MiMakeSystemAddressValid(PointerPte
, Process
);
76 /* It is, skip it and move to the next PDE, unless we're done */
78 PointerPte
= MiPteToAddress(PointerPde
);
79 if (PointerPte
> LastPte
) return CommittedPages
;
82 /* Now loop all the PTEs in the range */
83 while (PointerPte
<= LastPte
)
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte
))
88 /* Is this PDE demand zero? */
89 PointerPde
= MiAddressToPte(PointerPte
);
90 if (PointerPde
->u
.Long
!= 0)
92 /* It isn't -- is it valid? */
93 if (PointerPde
->u
.Hard
.Valid
== 0)
95 /* Nope, fault it in */
96 PointerPte
= MiPteToAddress(PointerPde
);
97 MiMakeSystemAddressValid(PointerPte
, Process
);
102 /* It is, skip it and move to the next PDE */
104 PointerPte
= MiPteToAddress(PointerPde
);
109 /* Is this PTE demand zero? */
110 if (PointerPte
->u
.Long
!= 0)
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
114 (PointerPte
->u
.Hard
.Valid
== 0) &&
115 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
116 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
118 /* It is, so remove it from the count of commited pages */
123 /* Move to the next PTE */
127 /* Return how many committed pages there still are */
128 return CommittedPages
;
131 /* This is a non-commited VAD, so assume none of it is committed */
134 /* Is the PDE demand-zero? */
135 PointerPde
= MiAddressToPte(PointerPte
);
136 if (PointerPde
->u
.Long
!= 0)
138 /* It isn't -- is it invalid? */
139 if (PointerPde
->u
.Hard
.Valid
== 0)
141 /* It is, so page it in */
142 PointerPte
= MiPteToAddress(PointerPde
);
143 MiMakeSystemAddressValid(PointerPte
, Process
);
148 /* It is, so skip it and move to the next PDE */
150 PointerPte
= MiPteToAddress(PointerPde
);
151 if (PointerPte
> LastPte
) return CommittedPages
;
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte
<= LastPte
)
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte
))
160 /* Is this new PDE demand-zero? */
161 PointerPde
= MiAddressToPte(PointerPte
);
162 if (PointerPde
->u
.Long
!= 0)
164 /* It isn't. Is it valid? */
165 if (PointerPde
->u
.Hard
.Valid
== 0)
167 /* It isn't, so make it valid */
168 PointerPte
= MiPteToAddress(PointerPde
);
169 MiMakeSystemAddressValid(PointerPte
, Process
);
174 /* It is, so skip it and move to the next one */
176 PointerPte
= MiPteToAddress(PointerPde
);
181 /* Is this PTE demand-zero? */
182 if (PointerPte
->u
.Long
!= 0)
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
186 (PointerPte
->u
.Hard
.Valid
== 1) ||
187 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
188 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
190 /* It is! So we'll treat this as a committed page */
195 /* Move to the next PTE */
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages
;
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
206 IN PEPROCESS CurrentProcess
)
209 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
210 PETHREAD CurrentThread
= PsGetCurrentThread();
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
214 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
215 (PageTableVirtualAddress
> MmPagedPoolEnd
));
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE
);
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress
))
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
230 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
231 if (!NT_SUCCESS(Status
))
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
237 (ULONG_PTR
)CurrentProcess
,
238 (ULONG_PTR
)PageTableVirtualAddress
);
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess
,
247 /* This flag will be useful later when we do better locking */
251 /* Let caller know what the lock state is */
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
261 BOOLEAN LockChange
= FALSE
;
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress
))
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
273 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
274 if (!NT_SUCCESS(Status
))
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
281 (ULONG_PTR
)VirtualAddress
);
284 /* This flag will be useful later when we do better locking */
287 /* Lock the PFN database */
288 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
291 /* Let caller know what the lock state is */
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
298 IN PFN_NUMBER PageCount
,
300 OUT PPFN_NUMBER ValidPages
)
302 PFN_COUNT ActualPages
= 0;
303 PETHREAD CurrentThread
= PsGetCurrentThread();
305 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
315 /* Make sure there's some data about the page */
316 if (PointerPte
->u
.Long
)
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
320 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte
->u
.Hard
.Valid
)
325 /* Get the page PFN */
326 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
327 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1
->u1
.WsIndex
== 0);
332 /* Actual valid, legitimate, pages */
333 if (ValidPages
) (*ValidPages
)++;
335 /* Get the page table entry */
336 PageTableIndex
= Pfn1
->u4
.PteFrame
;
337 Pfn2
= MiGetPfnEntry(PageTableIndex
);
339 /* Lock the PFN database */
340 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1
);
344 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2
, PageTableIndex
);
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte
);
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
362 * Right now, we shouldn't expect any page file information in the PTE
364 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte
);
370 /* Actual legitimate pages */
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE
, TRUE
);
391 MiDeletePte(IN PMMPTE PointerPte
,
392 IN PVOID VirtualAddress
,
393 IN PEPROCESS CurrentProcess
,
394 IN PMMPTE PrototypePte
)
398 PFN_NUMBER PageFrameIndex
;
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
404 /* Capture the PTE */
405 TempPte
= *PointerPte
;
407 /* See if the PTE is valid */
408 if (TempPte
.u
.Hard
.Valid
== 0)
410 /* Prototype PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 if (TempPte
.u
.Soft
.Transition
)
414 /* Get the PFN entry */
415 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
416 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
418 DPRINT1("Pte %p is transitional!\n", PointerPte
);
420 /* Destroy the PTE */
421 MI_ERASE_PTE(PointerPte
);
423 /* Drop the reference on the page table. */
424 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
426 if (Pfn1
->u2
.ShareCount
== 0)
428 NT_ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
429 /* Mark the page temporarily as valid, we're going to make it free soon */
430 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
432 /* Bring it back into the free list */
433 MiInsertPageInFreeList(PageFrameIndex
);
439 /* Get the PFN entry */
440 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
441 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
443 /* Check if this is a valid, prototype PTE */
444 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
446 /* Get the PDE and make sure it's faulted in */
447 PointerPde
= MiPteToPde(PointerPte
);
448 if (PointerPde
->u
.Hard
.Valid
== 0)
450 #if (_MI_PAGING_LEVELS == 2)
451 /* Could be paged pool access from a new process -- synchronize the page directories */
452 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
455 /* The PDE must be valid at this point */
456 KeBugCheckEx(MEMORY_MANAGEMENT
,
458 (ULONG_PTR
)PointerPte
,
460 (ULONG_PTR
)VirtualAddress
);
462 #if (_MI_PAGING_LEVELS == 2)
465 /* Drop the share count */
466 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
468 /* Either a fork, or this is the shared user data page */
469 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
471 /* If it's not the shared user page, then crash, since there's no fork() yet */
472 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
473 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
475 /* Must be some sort of memory corruption */
476 KeBugCheckEx(MEMORY_MANAGEMENT
,
478 (ULONG_PTR
)PointerPte
,
479 (ULONG_PTR
)PrototypePte
,
480 (ULONG_PTR
)Pfn1
->PteAddress
);
485 MI_ERASE_PTE(PointerPte
);
489 /* Make sure the saved PTE address is valid */
490 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
492 /* The PFN entry is illegal, or invalid */
493 KeBugCheckEx(MEMORY_MANAGEMENT
,
495 (ULONG_PTR
)PointerPte
,
497 (ULONG_PTR
)Pfn1
->PteAddress
);
501 MI_ERASE_PTE(PointerPte
);
503 /* There should only be 1 shared reference count */
504 ASSERT(Pfn1
->u2
.ShareCount
== 1);
506 /* Drop the reference on the page table. */
507 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
509 /* Mark the PFN for deletion and dereference what should be the last ref */
510 MI_SET_PFN_DELETED(Pfn1
);
511 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
513 /* We should eventually do this */
514 //CurrentProcess->NumberOfPrivatePages--;
523 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
524 IN ULONG_PTR EndingAddress
,
527 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
530 PEPROCESS CurrentProcess
;
532 BOOLEAN AddressGap
= FALSE
;
533 PSUBSECTION Subsection
;
535 /* Get out if this is a fake VAD, RosMm will free the marea pages */
536 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
538 /* Grab the process and PTE/PDE for the address being deleted */
539 CurrentProcess
= PsGetCurrentProcess();
540 PointerPde
= MiAddressToPde(Va
);
541 PointerPte
= MiAddressToPte(Va
);
543 /* Check if this is a section VAD or a VM VAD */
544 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
546 /* Don't worry about prototypes */
547 PrototypePte
= LastPrototypePte
= NULL
;
551 /* Get the prototype PTE */
552 PrototypePte
= Vad
->FirstPrototypePte
;
553 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
556 /* In all cases, we don't support fork() yet */
557 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
559 /* Loop the PTE for each VA */
562 /* First keep going until we find a valid PDE */
563 while (!PointerPde
->u
.Long
)
565 /* There are gaps in the address space */
568 /* Still no valid PDE, try the next 4MB (or whatever) */
571 /* Update the PTE on this new boundary */
572 PointerPte
= MiPteToAddress(PointerPde
);
574 /* Check if all the PDEs are invalid, so there's nothing to free */
575 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
576 if (Va
> EndingAddress
) return;
579 /* Now check if the PDE is mapped in */
580 if (!PointerPde
->u
.Hard
.Valid
)
582 /* It isn't, so map it in */
583 PointerPte
= MiPteToAddress(PointerPde
);
584 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
587 /* Now we should have a valid PDE, mapped in, and still have some VA */
588 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
589 ASSERT(Va
<= EndingAddress
);
591 /* Check if this is a section VAD with gaps in it */
592 if ((AddressGap
) && (LastPrototypePte
))
594 /* We need to skip to the next correct prototype PTE */
595 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
597 /* And we need the subsection to skip to the next last prototype PTE */
598 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
602 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
606 /* No more subsections, we are done with prototype PTEs */
611 /* Lock the PFN Database while we delete the PTEs */
612 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
615 /* Capture the PDE and make sure it exists */
616 TempPte
= *PointerPte
;
619 MiDecrementPageTableReferences((PVOID
)Va
);
621 /* Check if the PTE is actually mapped in */
622 if (MI_IS_MAPPED_PTE(&TempPte
))
624 /* Are we dealing with section VAD? */
625 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
627 /* We need to skip to the next correct prototype PTE */
628 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
630 /* And we need the subsection to skip to the next last prototype PTE */
631 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
635 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
639 /* No more subsections, we are done with prototype PTEs */
644 /* Check for prototype PTE */
645 if ((TempPte
.u
.Hard
.Valid
== 0) &&
646 (TempPte
.u
.Soft
.Prototype
== 1))
649 MI_ERASE_PTE(PointerPte
);
653 /* Delete the PTE proper */
654 MiDeletePte(PointerPte
,
662 /* The PTE was never mapped, just nuke it here */
663 MI_ERASE_PTE(PointerPte
);
667 /* Update the address and PTE for it */
672 /* Making sure the PDE is still valid */
673 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
675 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
677 /* The PDE should still be valid at this point */
678 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
680 /* Check remaining PTE count (go back 1 page due to above loop) */
681 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
683 if (PointerPde
->u
.Long
!= 0)
685 /* Delete the PTE proper */
686 MiDeletePte(PointerPde
,
687 MiPteToAddress(PointerPde
),
693 /* Release the lock and get out if we're done */
694 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
695 if (Va
> EndingAddress
) return;
697 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
698 PointerPde
= MiAddressToPde(Va
);
704 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
705 OUT PBOOLEAN HaveBadAddress
,
706 OUT PULONG_PTR BadAddress
)
708 PEXCEPTION_RECORD ExceptionRecord
;
714 *HaveBadAddress
= FALSE
;
717 // Get the exception record
719 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
722 // Look at the exception code
724 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
725 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
726 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
729 // We can tell the address if we have more than one parameter
731 if (ExceptionRecord
->NumberParameters
> 1)
734 // Return the address
736 *HaveBadAddress
= TRUE
;
737 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
742 // Continue executing the next handler
744 return EXCEPTION_EXECUTE_HANDLER
;
749 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
750 IN PVOID SourceAddress
,
751 IN PEPROCESS TargetProcess
,
752 OUT PVOID TargetAddress
,
753 IN SIZE_T BufferSize
,
754 IN KPROCESSOR_MODE PreviousMode
,
755 OUT PSIZE_T ReturnSize
)
757 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
758 PMDL Mdl
= (PMDL
)MdlBuffer
;
759 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
760 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
761 volatile BOOLEAN PagesLocked
;
762 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
763 volatile PVOID MdlAddress
;
765 BOOLEAN HaveBadAddress
;
766 ULONG_PTR BadAddress
;
767 NTSTATUS Status
= STATUS_SUCCESS
;
771 // Calculate the maximum amount of data to move
773 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
774 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
775 CurrentSize
= TotalSize
;
776 RemainingSize
= BufferSize
;
779 // Loop as long as there is still data
781 while (RemainingSize
> 0)
784 // Check if this transfer will finish everything off
786 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
789 // Attach to the source address space
791 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
794 // Reset state for this pass
798 FailedInMoving
= FALSE
;
799 ASSERT(FailedInProbe
== FALSE
);
802 // Protect user-mode copy
807 // If this is our first time, probe the buffer
809 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
812 // Catch a failure here
814 FailedInProbe
= TRUE
;
819 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
824 FailedInProbe
= FALSE
;
828 // Initialize and probe and lock the MDL
830 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
831 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
837 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
846 // Use our SEH handler to pick this up
848 FailedInMapping
= TRUE
;
849 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
853 // Now let go of the source and grab to the target process
855 KeUnstackDetachProcess(&ApcState
);
856 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
859 // Check if this is our first time through
861 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
864 // Catch a failure here
866 FailedInProbe
= TRUE
;
871 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
876 FailedInProbe
= FALSE
;
880 // Now do the actual move
882 FailedInMoving
= TRUE
;
883 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
885 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
890 // Detach from whoever we may be attached to
892 KeUnstackDetachProcess(&ApcState
);
895 // Check if we had mapped the pages
897 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
900 // Check if we had locked the pages
902 if (PagesLocked
) MmUnlockPages(Mdl
);
905 // Check if we hit working set quota
907 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
912 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
916 // Check if we failed during the probe or mapping
918 if ((FailedInProbe
) || (FailedInMapping
))
923 Status
= _SEH2_GetExceptionCode();
924 _SEH2_YIELD(return Status
);
928 // Otherwise, we failed probably during the move
930 *ReturnSize
= BufferSize
- RemainingSize
;
934 // Check if we know exactly where we stopped copying
939 // Return the exact number of bytes copied
941 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
946 // Return partial copy
948 Status
= STATUS_PARTIAL_COPY
;
953 // Check for SEH status
955 if (Status
!= STATUS_SUCCESS
) return Status
;
958 // Detach from target
960 KeUnstackDetachProcess(&ApcState
);
965 MmUnmapLockedPages(MdlAddress
, Mdl
);
969 // Update location and size
971 RemainingSize
-= CurrentSize
;
972 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
973 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
979 *ReturnSize
= BufferSize
;
980 return STATUS_SUCCESS
;
985 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
986 IN PVOID SourceAddress
,
987 IN PEPROCESS TargetProcess
,
988 OUT PVOID TargetAddress
,
989 IN SIZE_T BufferSize
,
990 IN KPROCESSOR_MODE PreviousMode
,
991 OUT PSIZE_T ReturnSize
)
993 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
994 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
995 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
996 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
999 BOOLEAN HaveBadAddress
;
1000 ULONG_PTR BadAddress
;
1001 NTSTATUS Status
= STATUS_SUCCESS
;
1005 // Calculate the maximum amount of data to move
1007 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1008 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1009 CurrentSize
= TotalSize
;
1010 RemainingSize
= BufferSize
;
1013 // Check if we can use the stack
1015 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1020 PoolAddress
= (PVOID
)StackBuffer
;
1027 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1028 if (!PoolAddress
) ASSERT(FALSE
);
1029 HavePoolAddress
= TRUE
;
1033 // Loop as long as there is still data
1035 while (RemainingSize
> 0)
1038 // Check if this transfer will finish everything off
1040 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1043 // Attach to the source address space
1045 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1048 // Reset state for this pass
1050 FailedInMoving
= FALSE
;
1051 ASSERT(FailedInProbe
== FALSE
);
1054 // Protect user-mode copy
1059 // If this is our first time, probe the buffer
1061 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1064 // Catch a failure here
1066 FailedInProbe
= TRUE
;
1071 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1076 FailedInProbe
= FALSE
;
1082 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1085 // Now let go of the source and grab to the target process
1087 KeUnstackDetachProcess(&ApcState
);
1088 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1091 // Check if this is our first time through
1093 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1096 // Catch a failure here
1098 FailedInProbe
= TRUE
;
1103 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1108 FailedInProbe
= FALSE
;
1112 // Now do the actual move
1114 FailedInMoving
= TRUE
;
1115 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1117 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1122 // Detach from whoever we may be attached to
1124 KeUnstackDetachProcess(&ApcState
);
1127 // Check if we had allocated pool
1129 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1132 // Check if we failed during the probe
1139 Status
= _SEH2_GetExceptionCode();
1140 _SEH2_YIELD(return Status
);
1144 // Otherwise, we failed, probably during the move
1146 *ReturnSize
= BufferSize
- RemainingSize
;
1150 // Check if we know exactly where we stopped copying
1155 // Return the exact number of bytes copied
1157 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1162 // Return partial copy
1164 Status
= STATUS_PARTIAL_COPY
;
1169 // Check for SEH status
1171 if (Status
!= STATUS_SUCCESS
) return Status
;
1174 // Detach from target
1176 KeUnstackDetachProcess(&ApcState
);
1179 // Update location and size
1181 RemainingSize
-= CurrentSize
;
1182 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1183 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1188 // Check if we had allocated pool
1190 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1195 *ReturnSize
= BufferSize
;
1196 return STATUS_SUCCESS
;
1201 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1202 IN PVOID SourceAddress
,
1203 IN PEPROCESS TargetProcess
,
1204 OUT PVOID TargetAddress
,
1205 IN SIZE_T BufferSize
,
1206 IN KPROCESSOR_MODE PreviousMode
,
1207 OUT PSIZE_T ReturnSize
)
1210 PEPROCESS Process
= SourceProcess
;
1213 // Don't accept zero-sized buffers
1215 if (!BufferSize
) return STATUS_SUCCESS
;
1218 // If we are copying from ourselves, lock the target instead
1220 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1223 // Acquire rundown protection
1225 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1230 return STATUS_PROCESS_IS_TERMINATING
;
1234 // See if we should use the pool copy
1236 if (BufferSize
> MI_POOL_COPY_BYTES
)
1241 Status
= MiDoMappedCopy(SourceProcess
,
1254 Status
= MiDoPoolCopy(SourceProcess
,
1266 ExReleaseRundownProtection(&Process
->RundownProtect
);
1272 MmFlushVirtualMemory(IN PEPROCESS Process
,
1273 IN OUT PVOID
*BaseAddress
,
1274 IN OUT PSIZE_T RegionSize
,
1275 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1283 return STATUS_SUCCESS
;
1288 MiGetPageProtection(IN PMMPTE PointerPte
)
1292 PEPROCESS CurrentProcess
;
1293 PETHREAD CurrentThread
;
1294 BOOLEAN WsSafe
, WsShared
;
1299 /* Copy this PTE's contents */
1300 TempPte
= *PointerPte
;
1302 /* Assure it's not totally zero */
1303 ASSERT(TempPte
.u
.Long
);
1305 /* Check for a special prototype format */
1306 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1307 (TempPte
.u
.Soft
.Prototype
== 1))
1309 /* Check if the prototype PTE is not yet pointing to a PTE */
1310 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1312 /* The prototype PTE contains the protection */
1313 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1316 /* Get a pointer to the underlying shared PTE */
1317 PointerPte
= MiProtoPteToPte(&TempPte
);
1319 /* Since the PTE we want to read can be paged out at any time, we need
1320 to release the working set lock first, so that it can be paged in */
1321 CurrentThread
= PsGetCurrentThread();
1322 CurrentProcess
= PsGetCurrentProcess();
1323 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1328 /* Now read the PTE value */
1329 TempPte
= *PointerPte
;
1331 /* Check if that one is invalid */
1332 if (!TempPte
.u
.Hard
.Valid
)
1334 /* We get the protection directly from this PTE */
1335 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1339 /* The PTE is valid, so we might need to get the protection from
1340 the PFN. Lock the PFN database */
1341 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1343 /* Check if the PDE is still valid */
1344 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1346 /* It's not, make it valid */
1347 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1350 /* Now it's safe to read the PTE value again */
1351 TempPte
= *PointerPte
;
1352 ASSERT(TempPte
.u
.Long
!= 0);
1354 /* Check again if the PTE is invalid */
1355 if (!TempPte
.u
.Hard
.Valid
)
1357 /* The PTE is not valid, so we can use it's protection field */
1358 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1362 /* The PTE is valid, so we can find the protection in the
1363 OriginalPte field of the PFN */
1364 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1365 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1368 /* Release the PFN database */
1369 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1372 /* Lock the working set again */
1373 MiLockProcessWorkingSetForFault(CurrentProcess
,
1381 /* In the easy case of transition or demand zero PTE just return its protection */
1382 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1384 /* If we get here, the PTE is valid, so look up the page in PFN database */
1385 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1386 if (!Pfn
->u3
.e1
.PrototypePte
)
1388 /* Return protection of the original pte */
1389 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1390 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1393 /* This is software PTE */
1394 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1395 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1396 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1397 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1398 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1403 MiQueryAddressState(IN PVOID Va
,
1405 IN PEPROCESS TargetProcess
,
1406 OUT PULONG ReturnedProtect
,
1410 PMMPTE PointerPte
, ProtoPte
;
1412 #if (_MI_PAGING_LEVELS >= 3)
1415 #if (_MI_PAGING_LEVELS >= 4)
1418 MMPTE TempPte
, TempProtoPte
;
1419 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1420 ULONG State
= MEM_RESERVE
, Protect
= 0;
1421 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1422 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1424 /* Only normal VADs supported */
1425 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1427 /* Get the PDE and PTE for the address */
1428 PointerPde
= MiAddressToPde(Va
);
1429 PointerPte
= MiAddressToPte(Va
);
1430 #if (_MI_PAGING_LEVELS >= 3)
1431 PointerPpe
= MiAddressToPpe(Va
);
1433 #if (_MI_PAGING_LEVELS >= 4)
1434 PointerPxe
= MiAddressToPxe(Va
);
1437 /* Return the next range */
1438 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1442 #if (_MI_PAGING_LEVELS >= 4)
1443 /* Does the PXE exist? */
1444 if (PointerPxe
->u
.Long
== 0)
1446 /* It does not, next range starts at the next PXE */
1447 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1451 /* Is the PXE valid? */
1452 if (PointerPxe
->u
.Hard
.Valid
== 0)
1454 /* Is isn't, fault it in (make the PPE accessible) */
1455 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1458 #if (_MI_PAGING_LEVELS >= 3)
1459 /* Does the PPE exist? */
1460 if (PointerPpe
->u
.Long
== 0)
1462 /* It does not, next range starts at the next PPE */
1463 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1467 /* Is the PPE valid? */
1468 if (PointerPpe
->u
.Hard
.Valid
== 0)
1470 /* Is isn't, fault it in (make the PDE accessible) */
1471 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1475 /* Does the PDE exist? */
1476 if (PointerPde
->u
.Long
== 0)
1478 /* It does not, next range starts at the next PDE */
1479 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1483 /* Is the PDE valid? */
1484 if (PointerPde
->u
.Hard
.Valid
== 0)
1486 /* Is isn't, fault it in (make the PTE accessible) */
1487 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1490 /* We have a PTE that we can access now! */
1495 /* Is it safe to try reading the PTE? */
1498 /* FIXME: watch out for large pages */
1499 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1501 /* Capture the PTE */
1502 TempPte
= *PointerPte
;
1503 if (TempPte
.u
.Long
!= 0)
1505 /* The PTE is valid, so it's not zeroed out */
1506 DemandZeroPte
= FALSE
;
1508 /* Is it a decommited, invalid, or faulted PTE? */
1509 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1510 (TempPte
.u
.Hard
.Valid
== 0) &&
1511 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1512 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1514 /* Otherwise our defaults should hold */
1515 ASSERT(Protect
== 0);
1516 ASSERT(State
== MEM_RESERVE
);
1520 /* This means it's committed */
1523 /* We don't support these */
1524 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1525 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1526 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1528 /* Get protection state of this page */
1529 Protect
= MiGetPageProtection(PointerPte
);
1531 /* Check if this is an image-backed VAD */
1532 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1533 (TempPte
.u
.Soft
.Prototype
== 1) &&
1534 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1537 DPRINT1("Not supported\n");
1544 /* Check if this was a demand-zero PTE, since we need to find the state */
1547 /* Not yet handled */
1548 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1549 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1551 /* Check if this is private commited memory, or an section-backed VAD */
1552 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1554 /* Tell caller about the next range */
1555 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1557 /* Get the prototype PTE for this VAD */
1558 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1559 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1562 /* We should unlock the working set, but it's not being held! */
1564 /* Is the prototype PTE actually valid (committed)? */
1565 TempProtoPte
= *ProtoPte
;
1566 if (TempProtoPte
.u
.Long
)
1568 /* Unless this is a memory-mapped file, handle it like private VAD */
1570 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1571 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1574 /* We should re-lock the working set */
1577 else if (Vad
->u
.VadFlags
.MemCommit
)
1579 /* This is committed memory */
1582 /* Convert the protection */
1583 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1587 /* Return the protection code */
1588 *ReturnedProtect
= Protect
;
1594 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1595 IN PVOID BaseAddress
,
1596 OUT PVOID MemoryInformation
,
1597 IN SIZE_T MemoryInformationLength
,
1598 OUT PSIZE_T ReturnLength
)
1600 PEPROCESS TargetProcess
;
1601 NTSTATUS Status
= STATUS_SUCCESS
;
1603 PVOID Address
, NextAddress
;
1604 BOOLEAN Found
= FALSE
;
1605 ULONG NewProtect
, NewState
;
1607 MEMORY_BASIC_INFORMATION MemoryInfo
;
1608 KAPC_STATE ApcState
;
1609 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1610 PMEMORY_AREA MemoryArea
;
1611 SIZE_T ResultLength
;
1613 /* Check for illegal addresses in user-space, or the shared memory area */
1614 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1615 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1617 Address
= PAGE_ALIGN(BaseAddress
);
1619 /* Make up an info structure describing this range */
1620 MemoryInfo
.BaseAddress
= Address
;
1621 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1622 MemoryInfo
.Type
= MEM_PRIVATE
;
1624 /* Special case for shared data */
1625 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1627 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1628 MemoryInfo
.State
= MEM_COMMIT
;
1629 MemoryInfo
.Protect
= PAGE_READONLY
;
1630 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1634 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1635 MemoryInfo
.State
= MEM_RESERVE
;
1636 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1637 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1640 /* Return the data, NtQueryInformation already probed it*/
1641 if (PreviousMode
!= KernelMode
)
1645 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1646 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1648 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1650 Status
= _SEH2_GetExceptionCode();
1656 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1657 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1663 /* Check if this is for a local or remote process */
1664 if (ProcessHandle
== NtCurrentProcess())
1666 TargetProcess
= PsGetCurrentProcess();
1670 /* Reference the target process */
1671 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1672 PROCESS_QUERY_INFORMATION
,
1674 ExGetPreviousMode(),
1675 (PVOID
*)&TargetProcess
,
1677 if (!NT_SUCCESS(Status
)) return Status
;
1679 /* Attach to it now */
1680 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1683 /* Lock the address space and make sure the process isn't already dead */
1684 MmLockAddressSpace(&TargetProcess
->Vm
);
1685 if (TargetProcess
->VmDeleted
)
1687 /* Unlock the address space of the process */
1688 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1690 /* Check if we were attached */
1691 if (ProcessHandle
!= NtCurrentProcess())
1693 /* Detach and dereference the process */
1694 KeUnstackDetachProcess(&ApcState
);
1695 ObDereferenceObject(TargetProcess
);
1699 DPRINT1("Process is dying\n");
1700 return STATUS_PROCESS_IS_TERMINATING
;
1704 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1705 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1707 /* Scan on the right */
1708 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1709 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1712 /* Check if this VAD covers the allocation range */
1713 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1714 (BaseVpn
<= Vad
->EndingVpn
))
1721 /* Check if this VAD is too high */
1722 if (BaseVpn
< Vad
->StartingVpn
)
1724 /* Stop if there is no left child */
1725 if (!Vad
->LeftChild
) break;
1727 /* Search on the left next */
1728 Vad
= Vad
->LeftChild
;
1732 /* Then this VAD is too low, keep searching on the right */
1733 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1735 /* Stop if there is no right child */
1736 if (!Vad
->RightChild
) break;
1738 /* Search on the right next */
1739 Vad
= Vad
->RightChild
;
1744 /* Was a VAD found? */
1747 Address
= PAGE_ALIGN(BaseAddress
);
1749 /* Calculate region size */
1752 if (Vad
->StartingVpn
>= BaseVpn
)
1754 /* Region size is the free space till the start of that VAD */
1755 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1759 /* Get the next VAD */
1760 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1763 /* Region size is the free space till the start of that VAD */
1764 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1768 /* Maximum possible region size with that base address */
1769 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1775 /* Maximum possible region size with that base address */
1776 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1779 /* Unlock the address space of the process */
1780 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1782 /* Check if we were attached */
1783 if (ProcessHandle
!= NtCurrentProcess())
1785 /* Detach and derefernece the process */
1786 KeUnstackDetachProcess(&ApcState
);
1787 ObDereferenceObject(TargetProcess
);
1790 /* Build the rest of the initial information block */
1791 MemoryInfo
.BaseAddress
= Address
;
1792 MemoryInfo
.AllocationBase
= NULL
;
1793 MemoryInfo
.AllocationProtect
= 0;
1794 MemoryInfo
.State
= MEM_FREE
;
1795 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1796 MemoryInfo
.Type
= 0;
1798 /* Return the data, NtQueryInformation already probed it*/
1799 if (PreviousMode
!= KernelMode
)
1803 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1804 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1806 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1808 Status
= _SEH2_GetExceptionCode();
1814 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1815 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1821 /* Set the correct memory type based on what kind of VAD this is */
1822 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1823 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1825 MemoryInfo
.Type
= MEM_PRIVATE
;
1827 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1829 MemoryInfo
.Type
= MEM_IMAGE
;
1833 MemoryInfo
.Type
= MEM_MAPPED
;
1836 /* Find the memory area the specified address belongs to */
1837 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1838 ASSERT(MemoryArea
!= NULL
);
1840 /* Determine information dependent on the memory area type */
1841 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1843 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1844 if (!NT_SUCCESS(Status
))
1846 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1847 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1848 NT_ASSERT(NT_SUCCESS(Status
));
1853 /* Build the initial information block */
1854 Address
= PAGE_ALIGN(BaseAddress
);
1855 MemoryInfo
.BaseAddress
= Address
;
1856 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1857 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1858 MemoryInfo
.Type
= MEM_PRIVATE
;
1860 /* Acquire the working set lock (shared is enough) */
1861 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1863 /* Find the largest chunk of memory which has the same state and protection mask */
1864 MemoryInfo
.State
= MiQueryAddressState(Address
,
1867 &MemoryInfo
.Protect
,
1869 Address
= NextAddress
;
1870 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1872 /* Keep going unless the state or protection mask changed */
1873 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1874 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1875 Address
= NextAddress
;
1878 /* Release the working set lock */
1879 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1881 /* Check if we went outside of the VAD */
1882 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1884 /* Set the end of the VAD as the end address */
1885 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1888 /* Now that we know the last VA address, calculate the region size */
1889 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1892 /* Unlock the address space of the process */
1893 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1895 /* Check if we were attached */
1896 if (ProcessHandle
!= NtCurrentProcess())
1898 /* Detach and derefernece the process */
1899 KeUnstackDetachProcess(&ApcState
);
1900 ObDereferenceObject(TargetProcess
);
1903 /* Return the data, NtQueryInformation already probed it */
1904 if (PreviousMode
!= KernelMode
)
1908 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1909 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1911 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1913 Status
= _SEH2_GetExceptionCode();
1919 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1920 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1924 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1925 "State: %lx Type: %lx Size: %lx\n",
1926 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1927 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1928 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1935 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1936 IN ULONG_PTR EndingAddress
,
1938 IN PEPROCESS Process
)
1940 PMMPTE PointerPte
, LastPte
, PointerPde
;
1941 BOOLEAN OnBoundary
= TRUE
;
1944 /* Get the PDE and PTE addresses */
1945 PointerPde
= MiAddressToPde(StartingAddress
);
1946 PointerPte
= MiAddressToPte(StartingAddress
);
1947 LastPte
= MiAddressToPte(EndingAddress
);
1949 /* Loop all the PTEs */
1950 while (PointerPte
<= LastPte
)
1952 /* Check if we've hit an new PDE boundary */
1955 /* Is this PDE demand zero? */
1956 PointerPde
= MiAddressToPte(PointerPte
);
1957 if (PointerPde
->u
.Long
!= 0)
1959 /* It isn't -- is it valid? */
1960 if (PointerPde
->u
.Hard
.Valid
== 0)
1962 /* Nope, fault it in */
1963 PointerPte
= MiPteToAddress(PointerPde
);
1964 MiMakeSystemAddressValid(PointerPte
, Process
);
1969 /* The PTE was already valid, so move to the next one */
1971 PointerPte
= MiPteToAddress(PointerPde
);
1973 /* Is the entire VAD committed? If not, fail */
1974 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1976 /* Everything is committed so far past the range, return true */
1977 if (PointerPte
> LastPte
) return TRUE
;
1981 /* Is the PTE demand zero? */
1982 if (PointerPte
->u
.Long
== 0)
1984 /* Is the entire VAD committed? If not, fail */
1985 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1989 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1990 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1991 (PointerPte
->u
.Hard
.Valid
== 0) &&
1992 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1993 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1995 /* Then part of the range is decommitted, so fail */
2000 /* Move to the next PTE */
2002 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2005 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2011 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2012 IN OUT PVOID
*BaseAddress
,
2013 IN OUT PSIZE_T NumberOfBytesToProtect
,
2014 IN ULONG NewAccessProtection
,
2015 OUT PULONG OldAccessProtection OPTIONAL
)
2017 PMEMORY_AREA MemoryArea
;
2018 PMMSUPPORT AddressSpace
;
2019 ULONG OldAccessProtection_
;
2022 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2023 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2025 AddressSpace
= &Process
->Vm
;
2026 MmLockAddressSpace(AddressSpace
);
2027 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2028 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2030 MmUnlockAddressSpace(AddressSpace
);
2031 return STATUS_UNSUCCESSFUL
;
2034 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2036 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2037 Status
= MmProtectSectionView(AddressSpace
,
2040 *NumberOfBytesToProtect
,
2041 NewAccessProtection
,
2042 OldAccessProtection
);
2044 MmUnlockAddressSpace(AddressSpace
);
2051 MiProtectVirtualMemory(IN PEPROCESS Process
,
2052 IN OUT PVOID
*BaseAddress
,
2053 IN OUT PSIZE_T NumberOfBytesToProtect
,
2054 IN ULONG NewAccessProtection
,
2055 OUT PULONG OldAccessProtection OPTIONAL
)
2057 PMEMORY_AREA MemoryArea
;
2059 PMMSUPPORT AddressSpace
;
2060 ULONG_PTR StartingAddress
, EndingAddress
;
2061 PMMPTE PointerPde
, PointerPte
, LastPte
;
2064 ULONG ProtectionMask
, OldProtect
;
2066 NTSTATUS Status
= STATUS_SUCCESS
;
2067 PETHREAD Thread
= PsGetCurrentThread();
2068 TABLE_SEARCH_RESULT Result
;
2070 /* Calculate base address for the VAD */
2071 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2072 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2074 /* Calculate the protection mask and make sure it's valid */
2075 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2076 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2078 DPRINT1("Invalid protection mask\n");
2079 return STATUS_INVALID_PAGE_PROTECTION
;
2082 /* Check for ROS specific memory area */
2083 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2084 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2087 return MiRosProtectVirtualMemory(Process
,
2089 NumberOfBytesToProtect
,
2090 NewAccessProtection
,
2091 OldAccessProtection
);
2094 /* Lock the address space and make sure the process isn't already dead */
2095 AddressSpace
= MmGetCurrentAddressSpace();
2096 MmLockAddressSpace(AddressSpace
);
2097 if (Process
->VmDeleted
)
2099 DPRINT1("Process is dying\n");
2100 Status
= STATUS_PROCESS_IS_TERMINATING
;
2104 /* Get the VAD for this address range, and make sure it exists */
2105 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2106 EndingAddress
>> PAGE_SHIFT
,
2108 (PMMADDRESS_NODE
*)&Vad
);
2109 if (Result
!= TableFoundNode
)
2111 DPRINT("Could not find a VAD for this allocation\n");
2112 Status
= STATUS_CONFLICTING_ADDRESSES
;
2116 /* Make sure the address is within this VAD's boundaries */
2117 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2118 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2120 Status
= STATUS_CONFLICTING_ADDRESSES
;
2124 /* These kinds of VADs are not supported atm */
2125 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2126 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2127 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2129 DPRINT1("Illegal VAD for attempting to set protection\n");
2130 Status
= STATUS_CONFLICTING_ADDRESSES
;
2134 /* Check for a VAD whose protection can't be changed */
2135 if (Vad
->u
.VadFlags
.NoChange
== 1)
2137 DPRINT1("Trying to change protection of a NoChange VAD\n");
2138 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2142 /* Is this section, or private memory? */
2143 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2145 /* Not yet supported */
2146 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2148 DPRINT1("Illegal VAD for attempting to set protection\n");
2149 Status
= STATUS_CONFLICTING_ADDRESSES
;
2153 /* Rotate VADs are not yet supported */
2154 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2156 DPRINT1("Illegal VAD for attempting to set protection\n");
2157 Status
= STATUS_CONFLICTING_ADDRESSES
;
2161 /* Not valid on section files */
2162 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2165 DPRINT1("Invalid protection flags for section\n");
2166 Status
= STATUS_INVALID_PARAMETER_4
;
2170 /* Check if data or page file mapping protection PTE is compatible */
2171 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2174 DPRINT1("Fixme: Not checking for valid protection\n");
2177 /* This is a section, and this is not yet supported */
2178 DPRINT1("Section protection not yet supported\n");
2183 /* Private memory, check protection flags */
2184 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2185 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2187 DPRINT1("Invalid protection flags for private memory\n");
2188 Status
= STATUS_INVALID_PARAMETER_4
;
2192 /* Lock the working set */
2193 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2195 /* Check if all pages in this range are committed */
2196 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2203 DPRINT1("The entire range is not committed\n");
2204 Status
= STATUS_NOT_COMMITTED
;
2205 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2209 /* Compute starting and ending PTE and PDE addresses */
2210 PointerPde
= MiAddressToPde(StartingAddress
);
2211 PointerPte
= MiAddressToPte(StartingAddress
);
2212 LastPte
= MiAddressToPte(EndingAddress
);
2214 /* Make this PDE valid */
2215 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2217 /* Save protection of the first page */
2218 if (PointerPte
->u
.Long
!= 0)
2220 /* Capture the page protection and make the PDE valid */
2221 OldProtect
= MiGetPageProtection(PointerPte
);
2222 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2226 /* Grab the old protection from the VAD itself */
2227 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2230 /* Loop all the PTEs now */
2231 while (PointerPte
<= LastPte
)
2233 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2234 if (MiIsPteOnPdeBoundary(PointerPte
))
2236 PointerPde
= MiAddressToPte(PointerPte
);
2237 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2240 /* Capture the PTE and check if it was empty */
2241 PteContents
= *PointerPte
;
2242 if (PteContents
.u
.Long
== 0)
2244 /* This used to be a zero PTE and it no longer is, so we must add a
2245 reference to the pagetable. */
2246 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2249 /* Check what kind of PTE we are dealing with */
2250 if (PteContents
.u
.Hard
.Valid
== 1)
2252 /* Get the PFN entry */
2253 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2255 /* We don't support this yet */
2256 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2258 /* Check if the page should not be accessible at all */
2259 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2260 (NewAccessProtection
& PAGE_GUARD
))
2262 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2264 /* Mark the PTE as transition and change its protection */
2265 PteContents
.u
.Hard
.Valid
= 0;
2266 PteContents
.u
.Soft
.Transition
= 1;
2267 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2268 /* Decrease PFN share count and write the PTE */
2269 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2270 // FIXME: remove the page from the WS
2271 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2273 // FIXME: Should invalidate entry in every CPU TLB
2276 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2278 /* We are done for this PTE */
2279 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2283 /* Write the protection mask and write it with a TLB flush */
2284 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2285 MiFlushTbAndCapture(Vad
,
2294 /* We don't support these cases yet */
2295 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2296 //ASSERT(PteContents.u.Soft.Transition == 0);
2298 /* The PTE is already demand-zero, just update the protection mask */
2299 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2300 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2301 ASSERT(PointerPte
->u
.Long
!= 0);
2304 /* Move to the next PTE */
2308 /* Unlock the working set */
2309 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2312 /* Unlock the address space */
2313 MmUnlockAddressSpace(AddressSpace
);
2315 /* Return parameters and success */
2316 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2317 *BaseAddress
= (PVOID
)StartingAddress
;
2318 *OldAccessProtection
= OldProtect
;
2319 return STATUS_SUCCESS
;
2322 /* Unlock the address space and return the failure code */
2323 MmUnlockAddressSpace(AddressSpace
);
2329 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2330 IN PEPROCESS TargetProcess
,
2333 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2336 // Sanity checks. The latter is because we only use this function with the
2337 // PFN lock not held, so it may go away in the future.
2339 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2340 ASSERT(OldIrql
== MM_NOIRQL
);
2343 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2344 // return the same address as the PDE on 2-level page table systems.
2346 // If everything is already valid, there is nothing to do.
2348 PointerPpe
= MiAddressToPte(PointerPde
);
2349 PointerPxe
= MiAddressToPde(PointerPde
);
2350 if ((PointerPxe
->u
.Hard
.Valid
) &&
2351 (PointerPpe
->u
.Hard
.Valid
) &&
2352 (PointerPde
->u
.Hard
.Valid
))
2358 // At least something is invalid, so begin by getting the PTE for the PDE itself
2359 // and then lookup each additional level. We must do it in this precise order
2360 // because the pagfault.c code (as well as in Windows) depends that the next
2361 // level up (higher) must be valid when faulting a lower level
2363 PointerPte
= MiPteToAddress(PointerPde
);
2367 // Make sure APCs continued to be disabled
2369 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2372 // First, make the PXE valid if needed
2374 if (!PointerPxe
->u
.Hard
.Valid
)
2376 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2377 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2383 if (!PointerPpe
->u
.Hard
.Valid
)
2385 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2386 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2390 // And finally, make the PDE itself valid.
2392 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2395 // This should've worked the first time so the loop is really just for
2396 // show -- ASSERT that we're actually NOT going to be looping.
2398 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2399 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2400 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2401 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2402 !(PointerPpe
->u
.Hard
.Valid
) ||
2403 !(PointerPde
->u
.Hard
.Valid
));
2408 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2414 PFN_NUMBER PageFrameIndex
;
2418 // Acquire the PFN lock and loop all the PTEs in the list
2420 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2421 for (i
= 0; i
!= Count
; i
++)
2424 // The PTE must currently be valid
2426 TempPte
= *ValidPteList
[i
];
2427 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2430 // Get the PFN entry for the page itself, and then for its page table
2432 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2433 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2434 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2437 // Decrement the share count on the page table, and then on the page
2440 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2441 MI_SET_PFN_DELETED(Pfn1
);
2442 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2445 // Make the page decommitted
2447 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2451 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2452 // and then release the PFN lock
2455 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2460 MiDecommitPages(IN PVOID StartingAddress
,
2461 IN PMMPTE EndingPte
,
2462 IN PEPROCESS Process
,
2465 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2466 ULONG CommitReduction
= 0;
2467 PMMPTE ValidPteList
[256];
2471 PETHREAD CurrentThread
= PsGetCurrentThread();
2474 // Get the PTE and PTE for the address, and lock the working set
2475 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2476 // commited range ends so that we can do the right accounting.
2478 PointerPde
= MiAddressToPde(StartingAddress
);
2479 PointerPte
= MiAddressToPte(StartingAddress
);
2480 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2481 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2484 // Make the PDE valid, and now loop through each page's worth of data
2486 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2487 while (PointerPte
<= EndingPte
)
2490 // Check if we've crossed a PDE boundary
2492 if (MiIsPteOnPdeBoundary(PointerPte
))
2495 // Get the new PDE and flush the valid PTEs we had built up until
2496 // now. This helps reduce the amount of TLB flushing we have to do.
2497 // Note that Windows does a much better job using timestamps and
2498 // such, and does not flush the entire TLB all the time, but right
2499 // now we have bigger problems to worry about than TLB flushing.
2501 PointerPde
= MiAddressToPde(StartingAddress
);
2504 MiProcessValidPteList(ValidPteList
, PteCount
);
2509 // Make this PDE valid
2511 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2515 // Read this PTE. It might be active or still demand-zero.
2517 PteContents
= *PointerPte
;
2518 if (PteContents
.u
.Long
)
2521 // The PTE is active. It might be valid and in a working set, or
2522 // it might be a prototype PTE or paged out or even in transition.
2524 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2527 // It's already decommited, so there's nothing for us to do here
2534 // Remove it from the counters, and check if it was valid or not
2536 //Process->NumberOfPrivatePages--;
2537 if (PteContents
.u
.Hard
.Valid
)
2540 // It's valid. At this point make sure that it is not a ROS
2541 // PFN. Also, we don't support ProtoPTEs in this code path.
2543 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2544 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2545 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2548 // Flush any pending PTEs that we had not yet flushed, if our
2549 // list has gotten too big, then add this PTE to the flush list.
2551 if (PteCount
== 256)
2553 MiProcessValidPteList(ValidPteList
, PteCount
);
2556 ValidPteList
[PteCount
++] = PointerPte
;
2561 // We do not support any of these other scenarios at the moment
2563 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2564 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2565 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2568 // So the only other possibility is that it is still a demand
2569 // zero PTE, in which case we undo the accounting we did
2570 // earlier and simply make the page decommitted.
2572 //Process->NumberOfPrivatePages++;
2573 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2580 // This used to be a zero PTE and it no longer is, so we must add a
2581 // reference to the pagetable.
2583 MiIncrementPageTableReferences(StartingAddress
);
2586 // Next, we account for decommitted PTEs and make the PTE as such
2588 if (PointerPte
> CommitPte
) CommitReduction
++;
2589 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2593 // Move to the next PTE and the next address
2596 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2600 // Flush any dangling PTEs from the loop in the last page table, and then
2601 // release the working set and return the commit reduction accounting.
2603 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2604 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2605 return CommitReduction
;
2608 /* PUBLIC FUNCTIONS ***********************************************************/
2615 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2626 MmSecureVirtualMemory(IN PVOID Address
,
2630 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2639 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2641 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2644 /* SYSTEM CALLS ***************************************************************/
2648 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2649 IN PVOID BaseAddress
,
2651 IN SIZE_T NumberOfBytesToRead
,
2652 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2654 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2656 NTSTATUS Status
= STATUS_SUCCESS
;
2657 SIZE_T BytesRead
= 0;
2661 // Check if we came from user mode
2663 if (PreviousMode
!= KernelMode
)
2666 // Validate the read addresses
2668 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2669 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2670 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2671 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2674 // Don't allow to write into kernel space
2676 return STATUS_ACCESS_VIOLATION
;
2680 // Enter SEH for probe
2685 // Probe the output value
2687 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2692 // Get exception code
2694 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2700 // Don't do zero-byte transfers
2702 if (NumberOfBytesToRead
)
2705 // Reference the process
2707 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2713 if (NT_SUCCESS(Status
))
2718 Status
= MmCopyVirtualMemory(Process
,
2720 PsGetCurrentProcess(),
2722 NumberOfBytesToRead
,
2727 // Dereference the process
2729 ObDereferenceObject(Process
);
2734 // Check if the caller sent this parameter
2736 if (NumberOfBytesRead
)
2739 // Enter SEH to guard write
2744 // Return the number of bytes read
2746 *NumberOfBytesRead
= BytesRead
;
2748 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2762 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2763 IN PVOID BaseAddress
,
2765 IN SIZE_T NumberOfBytesToWrite
,
2766 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2768 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2770 NTSTATUS Status
= STATUS_SUCCESS
;
2771 SIZE_T BytesWritten
= 0;
2775 // Check if we came from user mode
2777 if (PreviousMode
!= KernelMode
)
2780 // Validate the read addresses
2782 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2783 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2784 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2785 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2788 // Don't allow to write into kernel space
2790 return STATUS_ACCESS_VIOLATION
;
2794 // Enter SEH for probe
2799 // Probe the output value
2801 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2803 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2806 // Get exception code
2808 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2814 // Don't do zero-byte transfers
2816 if (NumberOfBytesToWrite
)
2819 // Reference the process
2821 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2827 if (NT_SUCCESS(Status
))
2832 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2836 NumberOfBytesToWrite
,
2841 // Dereference the process
2843 ObDereferenceObject(Process
);
2848 // Check if the caller sent this parameter
2850 if (NumberOfBytesWritten
)
2853 // Enter SEH to guard write
2858 // Return the number of bytes written
2860 *NumberOfBytesWritten
= BytesWritten
;
2862 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2876 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2877 IN OUT PVOID
*UnsafeBaseAddress
,
2878 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2879 IN ULONG NewAccessProtection
,
2880 OUT PULONG UnsafeOldAccessProtection
)
2883 ULONG OldAccessProtection
;
2885 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2886 PVOID BaseAddress
= NULL
;
2887 SIZE_T NumberOfBytesToProtect
= 0;
2888 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2890 BOOLEAN Attached
= FALSE
;
2891 KAPC_STATE ApcState
;
2895 // Check for valid protection flags
2897 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2898 if (Protection
!= PAGE_NOACCESS
&&
2899 Protection
!= PAGE_READONLY
&&
2900 Protection
!= PAGE_READWRITE
&&
2901 Protection
!= PAGE_WRITECOPY
&&
2902 Protection
!= PAGE_EXECUTE
&&
2903 Protection
!= PAGE_EXECUTE_READ
&&
2904 Protection
!= PAGE_EXECUTE_READWRITE
&&
2905 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2910 return STATUS_INVALID_PAGE_PROTECTION
;
2914 // Check if we came from user mode
2916 if (PreviousMode
!= KernelMode
)
2919 // Enter SEH for probing
2924 // Validate all outputs
2926 ProbeForWritePointer(UnsafeBaseAddress
);
2927 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2928 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2933 BaseAddress
= *UnsafeBaseAddress
;
2934 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2936 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2939 // Get exception code
2941 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2950 BaseAddress
= *UnsafeBaseAddress
;
2951 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2955 // Catch illegal base address
2957 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2960 // Catch illegal region size
2962 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2967 return STATUS_INVALID_PARAMETER_3
;
2971 // 0 is also illegal
2973 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2976 // Get a reference to the process
2978 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2979 PROCESS_VM_OPERATION
,
2984 if (!NT_SUCCESS(Status
)) return Status
;
2987 // Check if we should attach
2989 if (CurrentProcess
!= Process
)
2994 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2999 // Do the actual work
3001 Status
= MiProtectVirtualMemory(Process
,
3003 &NumberOfBytesToProtect
,
3004 NewAccessProtection
,
3005 &OldAccessProtection
);
3010 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3013 // Release reference
3015 ObDereferenceObject(Process
);
3018 // Enter SEH to return data
3023 // Return data to user
3025 *UnsafeOldAccessProtection
= OldAccessProtection
;
3026 *UnsafeBaseAddress
= BaseAddress
;
3027 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3029 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3046 // HACK until we have proper WSLIST support
3047 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3049 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3051 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3063 // HACK until we have proper WSLIST support
3064 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3066 if (!Wsle
->u1
.e1
.LockedInWs
&&
3067 !Wsle
->u1
.e1
.LockedInMemory
)
3069 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3072 if (LockType
& MAP_PROCESS
)
3073 Wsle
->u1
.e1
.LockedInWs
= 1;
3074 if (LockType
& MAP_SYSTEM
)
3075 Wsle
->u1
.e1
.LockedInMemory
= 1;
3084 // HACK until we have proper WSLIST support
3085 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3087 if (LockType
& MAP_PROCESS
)
3088 Wsle
->u1
.e1
.LockedInWs
= 0;
3089 if (LockType
& MAP_SYSTEM
)
3090 Wsle
->u1
.e1
.LockedInMemory
= 0;
3092 if (!Wsle
->u1
.e1
.LockedInWs
&&
3093 !Wsle
->u1
.e1
.LockedInMemory
)
3095 MiDereferencePfnAndDropLockCount(Pfn1
);
3101 MiCheckVadsForLockOperation(
3102 _Inout_ PVOID
*BaseAddress
,
3103 _Inout_ PSIZE_T RegionSize
,
3104 _Inout_ PVOID
*EndAddress
)
3110 /* Get the base address and align the start address */
3111 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3112 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3113 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3115 /* First loop and check all VADs */
3116 CurrentVa
= *BaseAddress
;
3117 while (CurrentVa
< *EndAddress
)
3120 Vad
= MiLocateAddress(CurrentVa
);
3123 /// FIXME: this might be a memory area for a section view...
3124 return STATUS_ACCESS_VIOLATION
;
3127 /* Check VAD type */
3128 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3129 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3130 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3132 *EndAddress
= CurrentVa
;
3133 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3134 return STATUS_INCOMPATIBLE_FILE_MAP
;
3137 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3140 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3141 return STATUS_SUCCESS
;
3146 MiLockVirtualMemory(
3147 IN OUT PVOID
*BaseAddress
,
3148 IN OUT PSIZE_T RegionSize
,
3151 PEPROCESS CurrentProcess
;
3152 PMMSUPPORT AddressSpace
;
3153 PVOID CurrentVa
, EndAddress
;
3154 PMMPTE PointerPte
, LastPte
;
3156 #if (_MI_PAGING_LEVELS >= 3)
3159 #if (_MI_PAGING_LEVELS == 4)
3163 NTSTATUS Status
, TempStatus
;
3165 /* Lock the address space */
3166 AddressSpace
= MmGetCurrentAddressSpace();
3167 MmLockAddressSpace(AddressSpace
);
3169 /* Make sure we still have an address space */
3170 CurrentProcess
= PsGetCurrentProcess();
3171 if (CurrentProcess
->VmDeleted
)
3173 Status
= STATUS_PROCESS_IS_TERMINATING
;
3177 /* Check the VADs in the requested range */
3178 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3179 if (!NT_SUCCESS(Status
))
3184 /* Enter SEH for probing */
3187 /* Loop all pages and probe them */
3188 CurrentVa
= *BaseAddress
;
3189 while (CurrentVa
< EndAddress
)
3191 (void)(*(volatile CHAR
*)CurrentVa
);
3192 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3195 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3197 Status
= _SEH2_GetExceptionCode();
3202 /* All pages were accessible, since we hold the address space lock, nothing
3203 can be de-committed. Assume success for now. */
3204 Status
= STATUS_SUCCESS
;
3206 /* Get the PTE and PDE */
3207 PointerPte
= MiAddressToPte(*BaseAddress
);
3208 PointerPde
= MiAddressToPde(*BaseAddress
);
3209 #if (_MI_PAGING_LEVELS >= 3)
3210 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3212 #if (_MI_PAGING_LEVELS == 4)
3213 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3216 /* Get the last PTE */
3217 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3219 /* Lock the process working set */
3220 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3222 /* Loop the pages */
3225 /* Check for a page that is not accessible */
3227 #if (_MI_PAGING_LEVELS == 4)
3228 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3230 #if (_MI_PAGING_LEVELS >= 3)
3231 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3233 (PointerPde
->u
.Hard
.Valid
== 0) ||
3234 (PointerPte
->u
.Hard
.Valid
== 0))
3236 /* Release process working set */
3237 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3239 /* Access the page */
3240 CurrentVa
= MiPteToAddress(PointerPte
);
3242 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3243 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3244 if (!NT_SUCCESS(TempStatus
))
3246 // This should only happen, when remote backing storage is not accessible
3248 Status
= TempStatus
;
3252 /* Lock the process working set */
3253 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3257 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3258 ASSERT(Pfn1
!= NULL
);
3260 /* Check the previous lock status */
3261 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3263 Status
= STATUS_WAS_LOCKED
;
3267 MI_LOCK_VA(Pfn1
, MapType
);
3269 /* Go to the next PTE */
3272 /* Check if we're on a PDE boundary */
3273 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3274 #if (_MI_PAGING_LEVELS >= 3)
3275 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3277 #if (_MI_PAGING_LEVELS == 4)
3278 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3280 } while (PointerPte
<= LastPte
);
3282 /* Release process working set */
3283 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3286 /* Unlock address space */
3287 MmUnlockAddressSpace(AddressSpace
);
3294 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3295 IN OUT PVOID
*BaseAddress
,
3296 IN OUT PSIZE_T NumberOfBytesToLock
,
3300 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3302 BOOLEAN Attached
= FALSE
;
3303 KAPC_STATE ApcState
;
3304 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3305 PVOID CapturedBaseAddress
;
3306 SIZE_T CapturedBytesToLock
;
3312 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3315 // Invalid set of flags
3317 return STATUS_INVALID_PARAMETER
;
3321 // At least one flag must be specified
3323 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3328 return STATUS_INVALID_PARAMETER
;
3332 // Enter SEH for probing
3337 // Validate output data
3339 ProbeForWritePointer(BaseAddress
);
3340 ProbeForWriteSize_t(NumberOfBytesToLock
);
3345 CapturedBaseAddress
= *BaseAddress
;
3346 CapturedBytesToLock
= *NumberOfBytesToLock
;
3348 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3351 // Get exception code
3353 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3358 // Catch illegal base address
3360 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3363 // Catch illegal region size
3365 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3370 return STATUS_INVALID_PARAMETER
;
3374 // 0 is also illegal
3376 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3379 // Get a reference to the process
3381 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3382 PROCESS_VM_OPERATION
,
3387 if (!NT_SUCCESS(Status
)) return Status
;
3390 // Check if this is is system-mapped
3392 if (MapType
& MAP_SYSTEM
)
3395 // Check for required privilege
3397 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3400 // Fail: Don't have it
3402 ObDereferenceObject(Process
);
3403 return STATUS_PRIVILEGE_NOT_HELD
;
3408 // Check if we should attach
3410 if (CurrentProcess
!= Process
)
3415 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3420 // Call the internal function
3422 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3423 &CapturedBytesToLock
,
3429 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3432 // Release reference
3434 ObDereferenceObject(Process
);
3437 // Enter SEH to return data
3442 // Return data to user
3444 *BaseAddress
= CapturedBaseAddress
;
3445 *NumberOfBytesToLock
= CapturedBytesToLock
;
3447 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3450 // Get exception code
3452 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3465 MiUnlockVirtualMemory(
3466 IN OUT PVOID
*BaseAddress
,
3467 IN OUT PSIZE_T RegionSize
,
3470 PEPROCESS CurrentProcess
;
3471 PMMSUPPORT AddressSpace
;
3473 PMMPTE PointerPte
, LastPte
;
3475 #if (_MI_PAGING_LEVELS >= 3)
3478 #if (_MI_PAGING_LEVELS == 4)
3484 /* Lock the address space */
3485 AddressSpace
= MmGetCurrentAddressSpace();
3486 MmLockAddressSpace(AddressSpace
);
3488 /* Make sure we still have an address space */
3489 CurrentProcess
= PsGetCurrentProcess();
3490 if (CurrentProcess
->VmDeleted
)
3492 Status
= STATUS_PROCESS_IS_TERMINATING
;
3496 /* Check the VADs in the requested range */
3497 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3499 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3500 incompatible VAD we continue, like Windows does */
3501 if (Status
== STATUS_ACCESS_VIOLATION
)
3503 Status
= STATUS_NOT_LOCKED
;
3507 /* Get the PTE and PDE */
3508 PointerPte
= MiAddressToPte(*BaseAddress
);
3509 PointerPde
= MiAddressToPde(*BaseAddress
);
3510 #if (_MI_PAGING_LEVELS >= 3)
3511 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3513 #if (_MI_PAGING_LEVELS == 4)
3514 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3517 /* Get the last PTE */
3518 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3520 /* Lock the process working set */
3521 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3523 /* Loop the pages */
3526 /* Check for a page that is not present */
3528 #if (_MI_PAGING_LEVELS == 4)
3529 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3531 #if (_MI_PAGING_LEVELS >= 3)
3532 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3534 (PointerPde
->u
.Hard
.Valid
== 0) ||
3535 (PointerPte
->u
.Hard
.Valid
== 0))
3537 /* Remember it, but keep going */
3538 Status
= STATUS_NOT_LOCKED
;
3543 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3544 ASSERT(Pfn1
!= NULL
);
3546 /* Check if all of the requested locks are present */
3547 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3548 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3550 /* Remember it, but keep going */
3551 Status
= STATUS_NOT_LOCKED
;
3553 /* Check if no lock is present */
3554 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3556 DPRINT1("FIXME: Should remove the page from WS\n");
3561 /* Go to the next PTE */
3564 /* Check if we're on a PDE boundary */
3565 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3566 #if (_MI_PAGING_LEVELS >= 3)
3567 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3569 #if (_MI_PAGING_LEVELS == 4)
3570 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3572 } while (PointerPte
<= LastPte
);
3574 /* Check if we hit a page that was not locked */
3575 if (Status
== STATUS_NOT_LOCKED
)
3577 goto CleanupWithWsLock
;
3580 /* All pages in the region were locked, so unlock them all */
3582 /* Get the PTE and PDE */
3583 PointerPte
= MiAddressToPte(*BaseAddress
);
3584 PointerPde
= MiAddressToPde(*BaseAddress
);
3585 #if (_MI_PAGING_LEVELS >= 3)
3586 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3588 #if (_MI_PAGING_LEVELS == 4)
3589 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3592 /* Loop the pages */
3596 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3597 MI_UNLOCK_VA(Pfn1
, MapType
);
3599 /* Go to the next PTE */
3602 /* Check if we're on a PDE boundary */
3603 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3604 #if (_MI_PAGING_LEVELS >= 3)
3605 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3607 #if (_MI_PAGING_LEVELS == 4)
3608 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3610 } while (PointerPte
<= LastPte
);
3612 /* Everything is done */
3613 Status
= STATUS_SUCCESS
;
3617 /* Release process working set */
3618 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3621 /* Unlock address space */
3622 MmUnlockAddressSpace(AddressSpace
);
3630 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3631 IN OUT PVOID
*BaseAddress
,
3632 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3636 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3638 BOOLEAN Attached
= FALSE
;
3639 KAPC_STATE ApcState
;
3640 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3641 PVOID CapturedBaseAddress
;
3642 SIZE_T CapturedBytesToUnlock
;
3648 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3651 // Invalid set of flags
3653 return STATUS_INVALID_PARAMETER
;
3657 // At least one flag must be specified
3659 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3664 return STATUS_INVALID_PARAMETER
;
3668 // Enter SEH for probing
3673 // Validate output data
3675 ProbeForWritePointer(BaseAddress
);
3676 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3681 CapturedBaseAddress
= *BaseAddress
;
3682 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3687 // Get exception code
3689 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3694 // Catch illegal base address
3696 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3699 // Catch illegal region size
3701 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3706 return STATUS_INVALID_PARAMETER
;
3710 // 0 is also illegal
3712 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3715 // Get a reference to the process
3717 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3718 PROCESS_VM_OPERATION
,
3723 if (!NT_SUCCESS(Status
)) return Status
;
3726 // Check if this is is system-mapped
3728 if (MapType
& MAP_SYSTEM
)
3731 // Check for required privilege
3733 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3736 // Fail: Don't have it
3738 ObDereferenceObject(Process
);
3739 return STATUS_PRIVILEGE_NOT_HELD
;
3744 // Check if we should attach
3746 if (CurrentProcess
!= Process
)
3751 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3756 // Call the internal function
3758 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3759 &CapturedBytesToUnlock
,
3765 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3768 // Release reference
3770 ObDereferenceObject(Process
);
3773 // Enter SEH to return data
3778 // Return data to user
3780 *BaseAddress
= CapturedBaseAddress
;
3781 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3783 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3786 // Get exception code
3788 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3795 return STATUS_SUCCESS
;
3800 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3801 IN OUT PVOID
*BaseAddress
,
3802 IN OUT PSIZE_T NumberOfBytesToFlush
,
3803 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3807 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3808 PVOID CapturedBaseAddress
;
3809 SIZE_T CapturedBytesToFlush
;
3810 IO_STATUS_BLOCK LocalStatusBlock
;
3814 // Check if we came from user mode
3816 if (PreviousMode
!= KernelMode
)
3819 // Enter SEH for probing
3824 // Validate all outputs
3826 ProbeForWritePointer(BaseAddress
);
3827 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3828 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3833 CapturedBaseAddress
= *BaseAddress
;
3834 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3836 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3839 // Get exception code
3841 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3850 CapturedBaseAddress
= *BaseAddress
;
3851 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3855 // Catch illegal base address
3857 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3860 // Catch illegal region size
3862 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3867 return STATUS_INVALID_PARAMETER
;
3871 // Get a reference to the process
3873 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3874 PROCESS_VM_OPERATION
,
3879 if (!NT_SUCCESS(Status
)) return Status
;
3884 Status
= MmFlushVirtualMemory(Process
,
3885 &CapturedBaseAddress
,
3886 &CapturedBytesToFlush
,
3890 // Release reference
3892 ObDereferenceObject(Process
);
3895 // Enter SEH to return data
3900 // Return data to user
3902 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3903 *NumberOfBytesToFlush
= 0;
3904 *IoStatusBlock
= LocalStatusBlock
;
3906 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3922 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3924 IN PVOID BaseAddress
,
3925 IN SIZE_T RegionSize
,
3926 IN PVOID
*UserAddressArray
,
3927 OUT PULONG_PTR EntriesInUserAddressArray
,
3928 OUT PULONG Granularity
)
3933 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3934 ULONG_PTR CapturedEntryCount
;
3938 // Check if we came from user mode
3940 if (PreviousMode
!= KernelMode
)
3943 // Enter SEH for probing
3948 // Catch illegal base address
3950 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3953 // Catch illegal region size
3955 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3960 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
3964 // Validate all data
3966 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3967 ProbeForWriteUlong(Granularity
);
3972 CapturedEntryCount
= *EntriesInUserAddressArray
;
3975 // Must have a count
3977 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3980 // Can't be larger than the maximum
3982 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3987 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3991 // Probe the actual array
3993 ProbeForWrite(UserAddressArray
,
3994 CapturedEntryCount
* sizeof(PVOID
),
3997 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4000 // Get exception code
4002 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4011 CapturedEntryCount
= *EntriesInUserAddressArray
;
4012 ASSERT(CapturedEntryCount
!= 0);
4016 // Check if this is a local request
4018 if (ProcessHandle
== NtCurrentProcess())
4021 // No need to reference the process
4023 Process
= PsGetCurrentProcess();
4028 // Reference the target
4030 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4031 PROCESS_VM_OPERATION
,
4036 if (!NT_SUCCESS(Status
)) return Status
;
4040 // Compute the last address and validate it
4042 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4043 if (BaseAddress
> EndAddress
)
4048 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4049 return STATUS_INVALID_PARAMETER_4
;
4058 // Dereference if needed
4060 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4063 // Enter SEH to return data
4068 // Return data to user
4070 *EntriesInUserAddressArray
= 0;
4071 *Granularity
= PAGE_SIZE
;
4073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4076 // Get exception code
4078 Status
= _SEH2_GetExceptionCode();
4085 return STATUS_SUCCESS
;
4093 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4094 IN PVOID BaseAddress
,
4095 IN SIZE_T RegionSize
)
4100 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4101 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4104 // Catch illegal base address
4106 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4109 // Catch illegal region size
4111 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4116 return STATUS_INVALID_PARAMETER_3
;
4120 // Check if this is a local request
4122 if (ProcessHandle
== NtCurrentProcess())
4125 // No need to reference the process
4127 Process
= PsGetCurrentProcess();
4132 // Reference the target
4134 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4135 PROCESS_VM_OPERATION
,
4140 if (!NT_SUCCESS(Status
)) return Status
;
4144 // Compute the last address and validate it
4146 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4147 if (BaseAddress
> EndAddress
)
4152 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4153 return STATUS_INVALID_PARAMETER_3
;
4162 // Dereference if needed
4164 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4169 return STATUS_SUCCESS
;
4174 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4175 IN PVOID BaseAddress
,
4176 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4177 OUT PVOID MemoryInformation
,
4178 IN SIZE_T MemoryInformationLength
,
4179 OUT PSIZE_T ReturnLength
)
4181 NTSTATUS Status
= STATUS_SUCCESS
;
4182 KPROCESSOR_MODE PreviousMode
;
4184 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4186 /* Bail out if the address is invalid */
4187 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4189 /* Probe return buffer */
4190 PreviousMode
= ExGetPreviousMode();
4191 if (PreviousMode
!= KernelMode
)
4195 ProbeForWrite(MemoryInformation
,
4196 MemoryInformationLength
,
4199 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4201 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4203 Status
= _SEH2_GetExceptionCode();
4207 if (!NT_SUCCESS(Status
))
4213 switch(MemoryInformationClass
)
4215 case MemoryBasicInformation
:
4216 /* Validate the size information of the class */
4217 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4219 /* The size is invalid */
4220 return STATUS_INFO_LENGTH_MISMATCH
;
4222 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4225 MemoryInformationLength
,
4229 case MemorySectionName
:
4230 /* Validate the size information of the class */
4231 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4233 /* The size is invalid */
4234 return STATUS_INFO_LENGTH_MISMATCH
;
4236 Status
= MiQueryMemorySectionName(ProcessHandle
,
4239 MemoryInformationLength
,
4242 case MemoryWorkingSetList
:
4243 case MemoryBasicVlmInformation
:
4245 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4257 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4258 IN OUT PVOID
* UBaseAddress
,
4259 IN ULONG_PTR ZeroBits
,
4260 IN OUT PSIZE_T URegionSize
,
4261 IN ULONG AllocationType
,
4265 PMEMORY_AREA MemoryArea
;
4266 PFN_NUMBER PageCount
;
4267 PMMVAD Vad
= NULL
, FoundVad
;
4269 PMMSUPPORT AddressSpace
;
4271 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
, HighestAddress
;
4272 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4273 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4274 PETHREAD CurrentThread
= PsGetCurrentThread();
4275 KAPC_STATE ApcState
;
4276 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4277 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4279 PMMPTE PointerPte
, PointerPde
, LastPte
;
4280 TABLE_SEARCH_RESULT Result
;
4281 PMMADDRESS_NODE Parent
;
4284 /* Check for valid Zero bits */
4285 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4287 DPRINT1("Too many zero bits\n");
4288 return STATUS_INVALID_PARAMETER_3
;
4291 /* Check for valid Allocation Types */
4292 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4293 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4295 DPRINT1("Invalid Allocation Type\n");
4296 return STATUS_INVALID_PARAMETER_5
;
4299 /* Check for at least one of these Allocation Types to be set */
4300 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4302 DPRINT1("No memory allocation base type\n");
4303 return STATUS_INVALID_PARAMETER_5
;
4306 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4307 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4309 DPRINT1("Invalid use of MEM_RESET\n");
4310 return STATUS_INVALID_PARAMETER_5
;
4313 /* Check if large pages are being used */
4314 if (AllocationType
& MEM_LARGE_PAGES
)
4316 /* Large page allocations MUST be committed */
4317 if (!(AllocationType
& MEM_COMMIT
))
4319 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4320 return STATUS_INVALID_PARAMETER_5
;
4323 /* These flags are not allowed with large page allocations */
4324 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4326 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4327 return STATUS_INVALID_PARAMETER_5
;
4331 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4332 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4334 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4335 return STATUS_INVALID_PARAMETER_5
;
4338 /* Check for valid MEM_PHYSICAL usage */
4339 if (AllocationType
& MEM_PHYSICAL
)
4341 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4342 if (!(AllocationType
& MEM_RESERVE
))
4344 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4345 return STATUS_INVALID_PARAMETER_5
;
4348 /* Only these flags are allowed with MEM_PHYSIAL */
4349 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4351 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4352 return STATUS_INVALID_PARAMETER_5
;
4355 /* Then make sure PAGE_READWRITE is used */
4356 if (Protect
!= PAGE_READWRITE
)
4358 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4359 return STATUS_INVALID_PARAMETER_6
;
4363 /* Calculate the protection mask and make sure it's valid */
4364 ProtectionMask
= MiMakeProtectionMask(Protect
);
4365 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4367 DPRINT1("Invalid protection mask\n");
4368 return STATUS_INVALID_PAGE_PROTECTION
;
4374 /* Check for user-mode parameters */
4375 if (PreviousMode
!= KernelMode
)
4377 /* Make sure they are writable */
4378 ProbeForWritePointer(UBaseAddress
);
4379 ProbeForWriteSize_t(URegionSize
);
4382 /* Capture their values */
4383 PBaseAddress
= *UBaseAddress
;
4384 PRegionSize
= *URegionSize
;
4386 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4388 /* Return the exception code */
4389 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4393 /* Make sure the allocation isn't past the VAD area */
4394 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4396 DPRINT1("Virtual allocation base above User Space\n");
4397 return STATUS_INVALID_PARAMETER_2
;
4400 /* Make sure the allocation wouldn't overflow past the VAD area */
4401 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4403 DPRINT1("Region size would overflow into kernel-memory\n");
4404 return STATUS_INVALID_PARAMETER_4
;
4407 /* Make sure there's a size specified */
4410 DPRINT1("Region size is invalid (zero)\n");
4411 return STATUS_INVALID_PARAMETER_4
;
4415 // If this is for the current process, just use PsGetCurrentProcess
4417 if (ProcessHandle
== NtCurrentProcess())
4419 Process
= CurrentProcess
;
4424 // Otherwise, reference the process with VM rights and attach to it if
4425 // this isn't the current process. We must attach because we'll be touching
4426 // PTEs and PDEs that belong to user-mode memory, and also touching the
4427 // Working Set which is stored in Hyperspace.
4429 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4430 PROCESS_VM_OPERATION
,
4435 if (!NT_SUCCESS(Status
)) return Status
;
4436 if (CurrentProcess
!= Process
)
4438 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4443 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4444 Process
, PBaseAddress
, ZeroBits
, PRegionSize
, AllocationType
, Protect
);
4447 // Check for large page allocations and make sure that the required privilege
4448 // is being held, before attempting to handle them.
4450 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4451 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4453 /* Fail without it */
4454 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4455 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4456 goto FailPathNoLock
;
4460 // Fail on the things we don't yet support
4462 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4464 DPRINT1("MEM_LARGE_PAGES not supported\n");
4465 Status
= STATUS_INVALID_PARAMETER
;
4466 goto FailPathNoLock
;
4468 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4470 DPRINT1("MEM_PHYSICAL not supported\n");
4471 Status
= STATUS_INVALID_PARAMETER
;
4472 goto FailPathNoLock
;
4474 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4476 DPRINT1("MEM_WRITE_WATCH not supported\n");
4477 Status
= STATUS_INVALID_PARAMETER
;
4478 goto FailPathNoLock
;
4482 // Check if the caller is reserving memory, or committing memory and letting
4483 // us pick the base address
4485 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4488 // Do not allow COPY_ON_WRITE through this API
4490 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4492 DPRINT1("Copy on write not allowed through this path\n");
4493 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4494 goto FailPathNoLock
;
4498 // Does the caller have an address in mind, or is this a blind commit?
4503 // This is a blind commit, all we need is the region size
4505 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4506 PageCount
= BYTES_TO_PAGES(PRegionSize
);
4508 StartingAddress
= 0;
4511 // Check if ZeroBits were specified
4516 // Calculate the highest address and check if it's valid
4518 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4519 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4521 Status
= STATUS_INVALID_PARAMETER_3
;
4522 goto FailPathNoLock
;
4528 // Use the highest VAD address as maximum
4530 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4536 // This is a reservation, so compute the starting address on the
4537 // expected 64KB granularity, and see where the ending address will
4538 // fall based on the aligned address and the passed in region size
4540 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4541 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4542 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
4546 // Allocate and initialize the VAD
4548 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4551 DPRINT1("Failed to allocate a VAD!\n");
4552 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4553 goto FailPathNoLock
;
4556 Vad
->u
.LongFlags
= 0;
4557 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4558 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4559 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4560 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
4563 // Lock the address space and make sure the process isn't already dead
4565 AddressSpace
= MmGetCurrentAddressSpace();
4566 MmLockAddressSpace(AddressSpace
);
4567 if (Process
->VmDeleted
)
4569 Status
= STATUS_PROCESS_IS_TERMINATING
;
4574 // Did we have a base address? If no, find a valid address that is 64KB
4575 // aligned in the VAD tree. Otherwise, make sure that the address range
4576 // which was passed in isn't already conflicting with an existing address
4581 /* Which way should we search? */
4582 if ((AllocationType
& MEM_TOP_DOWN
) || Process
->VmTopDown
)
4584 /* Find an address top-down */
4585 Result
= MiFindEmptyAddressRangeDownTree(PRegionSize
,
4594 /* Find an address bottom-up */
4595 Result
= MiFindEmptyAddressRangeInTree(PRegionSize
,
4602 if (Result
== TableFoundNode
)
4604 Status
= STATUS_NO_MEMORY
;
4609 // Now we know where the allocation ends. Make sure it doesn't end up
4610 // somewhere in kernel mode.
4612 ASSERT(StartingAddress
!= 0);
4613 ASSERT(StartingAddress
< (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
);
4614 EndingAddress
= (StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4615 ASSERT(EndingAddress
> StartingAddress
);
4616 if (EndingAddress
> HighestAddress
)
4618 Status
= STATUS_NO_MEMORY
;
4624 /* Make sure it doesn't conflict with an existing allocation */
4625 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4626 EndingAddress
>> PAGE_SHIFT
,
4629 if (Result
== TableFoundNode
)
4632 // The address specified is in conflict!
4634 Status
= STATUS_CONFLICTING_ADDRESSES
;
4640 // Write out the VAD fields for this allocation
4642 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
4643 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
4646 // FIXME: Should setup VAD bitmap
4648 Status
= STATUS_SUCCESS
;
4651 // Lock the working set and insert the VAD into the process VAD tree
4653 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4654 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4655 Process
->VadRoot
.NodeHint
= Vad
;
4656 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
4657 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4660 // Make sure the actual region size is at least as big as the
4661 // requested region size and update the value
4663 ASSERT(PRegionSize
<= (EndingAddress
+ 1 - StartingAddress
));
4664 PRegionSize
= (EndingAddress
+ 1 - StartingAddress
);
4667 // Update the virtual size of the process, and if this is now the highest
4668 // virtual size we have ever seen, update the peak virtual size to reflect
4671 Process
->VirtualSize
+= PRegionSize
;
4672 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4674 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4678 // Release address space and detach and dereference the target process if
4679 // it was different from the current process
4681 MmUnlockAddressSpace(AddressSpace
);
4682 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4683 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4686 // Use SEH to write back the base address and the region size. In the case
4687 // of an exception, we do not return back the exception code, as the memory
4688 // *has* been allocated. The caller would now have to call VirtualQuery
4689 // or do some other similar trick to actually find out where its memory
4690 // allocation ended up
4694 *URegionSize
= PRegionSize
;
4695 *UBaseAddress
= (PVOID
)StartingAddress
;
4697 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4700 // Ignore exception!
4704 DPRINT("Reserved %x bytes at %p.\n", PRegionSize
, StartingAddress
);
4705 return STATUS_SUCCESS
;
4709 // This is a MEM_COMMIT on top of an existing address which must have been
4710 // MEM_RESERVED already. Compute the start and ending base addresses based
4711 // on the user input, and then compute the actual region size once all the
4712 // alignments have been done.
4714 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4715 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4716 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4719 // Lock the address space and make sure the process isn't already dead
4721 AddressSpace
= MmGetCurrentAddressSpace();
4722 MmLockAddressSpace(AddressSpace
);
4723 if (Process
->VmDeleted
)
4725 DPRINT1("Process is dying\n");
4726 Status
= STATUS_PROCESS_IS_TERMINATING
;
4731 // Get the VAD for this address range, and make sure it exists
4733 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4734 EndingAddress
>> PAGE_SHIFT
,
4736 (PMMADDRESS_NODE
*)&FoundVad
);
4737 if (Result
!= TableFoundNode
)
4739 DPRINT1("Could not find a VAD for this allocation\n");
4740 Status
= STATUS_CONFLICTING_ADDRESSES
;
4744 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4746 /// @todo HACK: pretend success
4747 DPRINT("MEM_RESET not supported\n");
4748 Status
= STATUS_SUCCESS
;
4753 // These kinds of VADs are illegal for this Windows function when trying to
4754 // commit an existing range
4756 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4757 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4758 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4760 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4761 Status
= STATUS_CONFLICTING_ADDRESSES
;
4766 // Make sure that this address range actually fits within the VAD for it
4768 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4769 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4771 DPRINT1("Address range does not fit into the VAD\n");
4772 Status
= STATUS_CONFLICTING_ADDRESSES
;
4777 // Make sure this is an ARM3 section
4779 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4780 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4782 DPRINT1("Illegal commit of non-ARM3 section!\n");
4783 Status
= STATUS_ALREADY_COMMITTED
;
4787 // Is this a previously reserved section being committed? If so, enter the
4788 // special section path
4790 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4793 // You cannot commit large page sections through this API
4795 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4797 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4798 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4803 // You can only use caching flags on a rotate VAD
4805 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4806 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4808 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4809 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4814 // We should make sure that the section's permissions aren't being
4817 if (FoundVad
->u
.VadFlags
.NoChange
)
4820 // Make sure it's okay to touch it
4821 // Note: The Windows 2003 kernel has a bug here, passing the
4822 // unaligned base address together with the aligned size,
4823 // potentially covering a region larger than the actual allocation.
4824 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4825 // For now we keep this behavior.
4826 // TODO: analyze possible implications, create test case
4828 Status
= MiCheckSecuredVad(FoundVad
,
4832 if (!NT_SUCCESS(Status
))
4834 DPRINT1("Secured VAD being messed around with\n");
4840 // ARM3 does not support file-backed sections, only shared memory
4842 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4845 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4847 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4848 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4850 DPRINT1("Invalid page protection for rotate VAD\n");
4851 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4856 // Compute PTE addresses and the quota charge, then grab the commit lock
4858 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4859 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4860 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4861 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4864 // Get the segment template PTE and start looping each page
4866 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4867 ASSERT(TempPte
.u
.Long
!= 0);
4868 while (PointerPte
<= LastPte
)
4871 // For each non-already-committed page, write the invalid template PTE
4873 if (PointerPte
->u
.Long
== 0)
4875 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4885 // Now do the commit accounting and release the lock
4887 ASSERT(QuotaCharge
>= QuotaFree
);
4888 QuotaCharge
-= QuotaFree
;
4889 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4890 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4893 // We are done with committing the section pages
4895 Status
= STATUS_SUCCESS
;
4900 // This is a specific ReactOS check because we only use normal VADs
4902 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4905 // While this is an actual Windows check
4907 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4910 // Throw out attempts to use copy-on-write through this API path
4912 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4914 DPRINT1("Write copy attempted when not allowed\n");
4915 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4920 // Initialize a demand-zero PTE
4923 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4924 NT_ASSERT(TempPte
.u
.Long
!= 0);
4927 // Get the PTE, PDE and the last PTE for this address range
4929 PointerPde
= MiAddressToPde(StartingAddress
);
4930 PointerPte
= MiAddressToPte(StartingAddress
);
4931 LastPte
= MiAddressToPte(EndingAddress
);
4934 // Update the commit charge in the VAD as well as in the process, and check
4935 // if this commit charge was now higher than the last recorded peak, in which
4936 // case we also update the peak
4938 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4939 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4940 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4942 Process
->CommitChargePeak
= Process
->CommitCharge
;
4946 // Lock the working set while we play with user pages and page tables
4948 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4951 // Make the current page table valid, and then loop each page within it
4953 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4954 while (PointerPte
<= LastPte
)
4957 // Have we crossed into a new page table?
4959 if (MiIsPteOnPdeBoundary(PointerPte
))
4962 // Get the PDE and now make it valid too
4964 PointerPde
= MiAddressToPte(PointerPte
);
4965 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4969 // Is this a zero PTE as expected?
4971 if (PointerPte
->u
.Long
== 0)
4974 // First increment the count of pages in the page table for this
4977 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4980 // And now write the invalid demand-zero PTE as requested
4982 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4984 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4987 // If the PTE was already decommitted, there is nothing else to do
4988 // but to write the new demand-zero PTE
4990 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4992 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4995 // We don't handle these scenarios yet
4997 if (PointerPte
->u
.Soft
.Valid
== 0)
4999 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
5000 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
5004 // There's a change in protection, remember this for later, but do
5005 // not yet handle it.
5007 ChangeProtection
= TRUE
;
5011 // Move to the next PTE
5017 // Release the working set lock, unlock the address space, and detach from
5018 // the target process if it was not the current process. Also dereference the
5019 // target process if this wasn't the case.
5021 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5022 Status
= STATUS_SUCCESS
;
5024 MmUnlockAddressSpace(AddressSpace
);
5026 if (!NT_SUCCESS(Status
))
5030 ExFreePoolWithTag(Vad
, 'SdaV');
5035 // Check if we need to update the protection
5037 if (ChangeProtection
)
5039 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
5040 SIZE_T ProtectSize
= PRegionSize
;
5041 ULONG OldProtection
;
5044 // Change the protection of the region
5046 MiProtectVirtualMemory(Process
,
5047 &ProtectBaseAddress
,
5054 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5055 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5058 // Only write back results on success
5060 if (NT_SUCCESS(Status
))
5063 // Use SEH to write back the base address and the region size. In the case
5064 // of an exception, we strangely do return back the exception code, even
5065 // though the memory *has* been allocated. This mimics Windows behavior and
5066 // there is not much we can do about it.
5070 *URegionSize
= PRegionSize
;
5071 *UBaseAddress
= (PVOID
)StartingAddress
;
5073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5075 Status
= _SEH2_GetExceptionCode();
5088 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5089 IN PVOID
* UBaseAddress
,
5090 IN PSIZE_T URegionSize
,
5093 PMEMORY_AREA MemoryArea
;
5096 LONG_PTR CommitReduction
= 0;
5097 ULONG_PTR StartingAddress
, EndingAddress
;
5101 PMMSUPPORT AddressSpace
;
5102 PETHREAD CurrentThread
= PsGetCurrentThread();
5103 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5104 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5105 KAPC_STATE ApcState
;
5106 BOOLEAN Attached
= FALSE
;
5110 // Only two flags are supported
5112 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5114 DPRINT1("Invalid FreeType\n");
5115 return STATUS_INVALID_PARAMETER_4
;
5119 // Check if no flag was used, or if both flags were used
5121 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5122 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5124 DPRINT1("Invalid FreeType combination\n");
5125 return STATUS_INVALID_PARAMETER_4
;
5129 // Enter SEH for probe and capture. On failure, return back to the caller
5130 // with an exception violation.
5135 // Check for user-mode parameters and make sure that they are writeable
5137 if (PreviousMode
!= KernelMode
)
5139 ProbeForWritePointer(UBaseAddress
);
5140 ProbeForWriteUlong(URegionSize
);
5144 // Capture the current values
5146 PBaseAddress
= *UBaseAddress
;
5147 PRegionSize
= *URegionSize
;
5149 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5151 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5156 // Make sure the allocation isn't past the user area
5158 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5160 DPRINT1("Virtual free base above User Space\n");
5161 return STATUS_INVALID_PARAMETER_2
;
5165 // Make sure the allocation wouldn't overflow past the user area
5167 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5169 DPRINT1("Region size would overflow into kernel-memory\n");
5170 return STATUS_INVALID_PARAMETER_3
;
5174 // If this is for the current process, just use PsGetCurrentProcess
5176 if (ProcessHandle
== NtCurrentProcess())
5178 Process
= CurrentProcess
;
5183 // Otherwise, reference the process with VM rights and attach to it if
5184 // this isn't the current process. We must attach because we'll be touching
5185 // PTEs and PDEs that belong to user-mode memory, and also touching the
5186 // Working Set which is stored in Hyperspace.
5188 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5189 PROCESS_VM_OPERATION
,
5194 if (!NT_SUCCESS(Status
)) return Status
;
5195 if (CurrentProcess
!= Process
)
5197 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5202 DPRINT("NtFreeVirtualMemory: Process 0x%p, Adress 0x%p, size 0x%x, FreeType %x.\n",
5203 Process
, PBaseAddress
, PRegionSize
, FreeType
);
5206 // Lock the address space
5208 AddressSpace
= MmGetCurrentAddressSpace();
5209 MmLockAddressSpace(AddressSpace
);
5212 // If the address space is being deleted, fail the de-allocation since it's
5213 // too late to do anything about it
5215 if (Process
->VmDeleted
)
5217 DPRINT1("Process is dead\n");
5218 Status
= STATUS_PROCESS_IS_TERMINATING
;
5223 // Compute start and end addresses, and locate the VAD
5225 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5226 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5227 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5230 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5231 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5236 // If the range exceeds the VAD's ending VPN, fail this request
5238 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5240 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5241 Status
= STATUS_UNABLE_TO_FREE_VM
;
5246 // Only private memory (except rotate VADs) can be freed through here */
5248 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5249 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5250 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5252 DPRINT1("Attempt to free section memory\n");
5253 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5258 // ARM3 does not yet handle protected VM
5260 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5263 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5264 // and that is is an ARM3 memory area, and not a section view, as we currently
5265 // don't support freeing those though this interface.
5267 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5269 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5272 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5274 if (FreeType
& MEM_RELEASE
)
5277 // ARM3 only supports this VAD in this path
5279 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5282 // Is the caller trying to remove the whole VAD, or remove only a portion
5283 // of it? If no region size is specified, then the assumption is that the
5284 // whole VAD is to be destroyed
5289 // The caller must specify the base address identically to the range
5290 // that is stored in the VAD.
5292 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5294 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5295 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5300 // Now compute the actual start/end addresses based on the VAD
5302 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5303 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5306 // Finally lock the working set and remove the VAD from the VAD tree
5308 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5309 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5310 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5315 // This means the caller wants to release a specific region within
5316 // the range. We have to find out which range this is -- the following
5317 // possibilities exist plus their union (CASE D):
5319 // STARTING ADDRESS ENDING ADDRESS
5320 // [<========][========================================][=========>]
5321 // CASE A CASE B CASE C
5324 // First, check for case A or D
5326 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5331 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5334 // This is the easiest one to handle -- it is identical to
5335 // the code path above when the caller sets a zero region size
5336 // and the whole VAD is destroyed
5338 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5339 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5340 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5345 // This case is pretty easy too -- we compute a bunch of
5346 // pages to decommit, and then push the VAD's starting address
5347 // a bit further down, then decrement the commit charge
5349 // NOT YET IMPLEMENTED IN ARM3.
5351 DPRINT1("Case A not handled\n");
5352 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5356 // After analyzing the VAD, set it to NULL so that we don't
5357 // free it in the exit path
5365 // This is case B or case C. First check for case C
5367 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5369 PMEMORY_AREA MemoryArea
;
5372 // This is pretty easy and similar to case A. We compute the
5373 // amount of pages to decommit, update the VAD's commit charge
5374 // and then change the ending address of the VAD to be a bit
5377 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5378 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5382 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5383 // For ReactOS: shrink the corresponding memory area
5384 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5385 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
5386 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
5387 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
5388 MemoryArea
->EndingAddress
= (PVOID
)(StartingAddress
);
5393 // This is case B and the hardest one. Because we are removing
5394 // a chunk of memory from the very middle of the VAD, we must
5395 // actually split the VAD into two new VADs and compute the
5396 // commit charges for each of them, and reinsert new charges.
5398 // NOT YET IMPLEMENTED IN ARM3.
5400 DPRINT1("Case B not handled\n");
5401 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5406 // After analyzing the VAD, set it to NULL so that we don't
5407 // free it in the exit path
5414 // Now we have a range of pages to dereference, so call the right API
5415 // to do that and then release the working set, since we're done messing
5416 // around with process pages.
5418 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5419 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5420 Status
= STATUS_SUCCESS
;
5424 // Update the process counters
5426 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5427 Process
->CommitCharge
-= CommitReduction
;
5428 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5431 // Unlock the address space and free the VAD in failure cases. Next,
5432 // detach from the target process so we can write the region size and the
5433 // base address to the correct source process, and dereference the target
5436 MmUnlockAddressSpace(AddressSpace
);
5437 if (Vad
) ExFreePool(Vad
);
5438 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5439 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5442 // Use SEH to safely return the region size and the base address of the
5443 // deallocation. If we get an access violation, don't return a failure code
5444 // as the deallocation *has* happened. The caller will just have to figure
5445 // out another way to find out where it is (such as VirtualQuery).
5449 *URegionSize
= PRegionSize
;
5450 *UBaseAddress
= (PVOID
)StartingAddress
;
5452 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5460 // This is the decommit path. You cannot decommit from the following VADs in
5461 // Windows, so fail the vall
5463 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5464 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5465 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5467 DPRINT1("Trying to decommit from invalid VAD\n");
5468 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5473 // If the caller did not specify a region size, first make sure that this
5474 // region is actually committed. If it is, then compute the ending address
5475 // based on the VAD.
5479 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5481 DPRINT1("Decomitting non-committed memory\n");
5482 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5485 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5489 // Decommit the PTEs for the range plus the actual backing pages for the
5490 // range, then reduce that amount from the commit charge in the VAD
5492 CommitReduction
= MiAddressToPte(EndingAddress
) -
5493 MiAddressToPte(StartingAddress
) +
5495 MiDecommitPages((PVOID
)StartingAddress
,
5496 MiAddressToPte(EndingAddress
),
5499 ASSERT(CommitReduction
>= 0);
5500 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5501 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5504 // We are done, go to the exit path without freeing the VAD as it remains
5505 // valid since we have not released the allocation.
5508 Status
= STATUS_SUCCESS
;
5512 // In the failure path, we detach and derefernece the target process, and
5513 // return whatever failure code was sent.
5516 MmUnlockAddressSpace(AddressSpace
);
5517 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5518 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5525 MmGetPhysicalAddress(PVOID Address
)
5527 PHYSICAL_ADDRESS PhysicalAddress
;
5531 /* Check if the PXE/PPE/PDE is valid */
5533 #if (_MI_PAGING_LEVELS == 4)
5534 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5536 #if (_MI_PAGING_LEVELS >= 3)
5537 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5539 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5541 /* Check for large pages */
5542 TempPde
= *MiAddressToPde(Address
);
5543 if (TempPde
.u
.Hard
.LargePage
)
5545 /* Physical address is base page + large page offset */
5546 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5547 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5548 return PhysicalAddress
;
5551 /* Check if the PTE is valid */
5552 TempPte
= *MiAddressToPte(Address
);
5553 if (TempPte
.u
.Hard
.Valid
)
5555 /* Physical address is base page + page offset */
5556 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5557 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5558 return PhysicalAddress
;
5562 KeRosDumpStackFrames(NULL
, 20);
5563 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5564 PhysicalAddress
.QuadPart
= 0;
5565 return PhysicalAddress
;