2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
10 /* So long, and Thanks for All the Fish */
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
32 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
34 IN ULONG ProtectionMask
,
36 IN BOOLEAN CaptureDirtyBit
);
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
44 IN ULONG_PTR EndingAddress
,
48 PMMPTE PointerPte
, LastPte
, PointerPde
;
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde
= MiAddressToPde(StartingAddress
);
53 PointerPte
= MiAddressToPte(StartingAddress
);
54 LastPte
= MiAddressToPte(EndingAddress
);
56 /* Handle commited pages first */
57 if (Vad
->u
.VadFlags
.MemCommit
== 1)
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages
= (ULONG
)BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
62 /* Is the PDE demand-zero? */
63 PointerPde
= MiAddressToPte(PointerPte
);
64 if (PointerPde
->u
.Long
!= 0)
66 /* It is not. Is it valid? */
67 if (PointerPde
->u
.Hard
.Valid
== 0)
70 PointerPte
= MiPteToAddress(PointerPde
);
71 MiMakeSystemAddressValid(PointerPte
, Process
);
76 /* It is, skip it and move to the next PDE, unless we're done */
78 PointerPte
= MiPteToAddress(PointerPde
);
79 if (PointerPte
> LastPte
) return CommittedPages
;
82 /* Now loop all the PTEs in the range */
83 while (PointerPte
<= LastPte
)
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte
))
88 /* Is this PDE demand zero? */
89 PointerPde
= MiAddressToPte(PointerPte
);
90 if (PointerPde
->u
.Long
!= 0)
92 /* It isn't -- is it valid? */
93 if (PointerPde
->u
.Hard
.Valid
== 0)
95 /* Nope, fault it in */
96 PointerPte
= MiPteToAddress(PointerPde
);
97 MiMakeSystemAddressValid(PointerPte
, Process
);
102 /* It is, skip it and move to the next PDE */
104 PointerPte
= MiPteToAddress(PointerPde
);
109 /* Is this PTE demand zero? */
110 if (PointerPte
->u
.Long
!= 0)
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
114 (PointerPte
->u
.Hard
.Valid
== 0) &&
115 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
116 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
118 /* It is, so remove it from the count of commited pages */
123 /* Move to the next PTE */
127 /* Return how many committed pages there still are */
128 return CommittedPages
;
131 /* This is a non-commited VAD, so assume none of it is committed */
134 /* Is the PDE demand-zero? */
135 PointerPde
= MiAddressToPte(PointerPte
);
136 if (PointerPde
->u
.Long
!= 0)
138 /* It isn't -- is it invalid? */
139 if (PointerPde
->u
.Hard
.Valid
== 0)
141 /* It is, so page it in */
142 PointerPte
= MiPteToAddress(PointerPde
);
143 MiMakeSystemAddressValid(PointerPte
, Process
);
148 /* It is, so skip it and move to the next PDE */
150 PointerPte
= MiPteToAddress(PointerPde
);
151 if (PointerPte
> LastPte
) return CommittedPages
;
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte
<= LastPte
)
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte
))
160 /* Is this new PDE demand-zero? */
161 PointerPde
= MiAddressToPte(PointerPte
);
162 if (PointerPde
->u
.Long
!= 0)
164 /* It isn't. Is it valid? */
165 if (PointerPde
->u
.Hard
.Valid
== 0)
167 /* It isn't, so make it valid */
168 PointerPte
= MiPteToAddress(PointerPde
);
169 MiMakeSystemAddressValid(PointerPte
, Process
);
174 /* It is, so skip it and move to the next one */
176 PointerPte
= MiPteToAddress(PointerPde
);
181 /* Is this PTE demand-zero? */
182 if (PointerPte
->u
.Long
!= 0)
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
186 (PointerPte
->u
.Hard
.Valid
== 1) ||
187 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
188 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
190 /* It is! So we'll treat this as a committed page */
195 /* Move to the next PTE */
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages
;
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
206 IN PEPROCESS CurrentProcess
)
209 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
210 PETHREAD CurrentThread
= PsGetCurrentThread();
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
214 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
215 (PageTableVirtualAddress
> MmPagedPoolEnd
));
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE
);
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress
))
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
230 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
231 if (!NT_SUCCESS(Status
))
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
237 (ULONG_PTR
)CurrentProcess
,
238 (ULONG_PTR
)PageTableVirtualAddress
);
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess
,
247 /* This flag will be useful later when we do better locking */
251 /* Let caller know what the lock state is */
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
261 BOOLEAN LockChange
= FALSE
;
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress
))
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
273 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
274 if (!NT_SUCCESS(Status
))
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
281 (ULONG_PTR
)VirtualAddress
);
284 /* This flag will be useful later when we do better locking */
287 /* Lock the PFN database */
288 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
291 /* Let caller know what the lock state is */
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
298 IN PFN_NUMBER PageCount
,
300 OUT PPFN_NUMBER ValidPages
)
302 PFN_COUNT ActualPages
= 0;
303 PETHREAD CurrentThread
= PsGetCurrentThread();
305 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
315 /* Make sure there's some data about the page */
316 if (PointerPte
->u
.Long
)
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
320 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte
->u
.Hard
.Valid
)
325 /* Get the page PFN */
326 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
327 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1
->u1
.WsIndex
== 0);
332 /* Actual valid, legitimate, pages */
333 if (ValidPages
) (*ValidPages
)++;
335 /* Get the page table entry */
336 PageTableIndex
= Pfn1
->u4
.PteFrame
;
337 Pfn2
= MiGetPfnEntry(PageTableIndex
);
339 /* Lock the PFN database */
340 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1
);
344 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2
, PageTableIndex
);
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte
);
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
362 * Right now, we shouldn't expect any page file information in the PTE
364 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte
);
370 /* Actual legitimate pages */
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE
, TRUE
);
391 MiDeletePte(IN PMMPTE PointerPte
,
392 IN PVOID VirtualAddress
,
393 IN PEPROCESS CurrentProcess
,
394 IN PMMPTE PrototypePte
)
398 PFN_NUMBER PageFrameIndex
;
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
404 /* Capture the PTE */
405 TempPte
= *PointerPte
;
407 /* We only support valid PTEs for now */
408 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
409 if (TempPte
.u
.Hard
.Valid
== 0)
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
413 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde
= MiPteToPde(PointerPte
);
425 if (PointerPde
->u
.Hard
.Valid
== 0)
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT
,
435 (ULONG_PTR
)PointerPte
,
437 (ULONG_PTR
)VirtualAddress
);
439 #if (_MI_PAGING_LEVELS == 2)
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
450 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT
,
455 (ULONG_PTR
)PointerPte
,
456 (ULONG_PTR
)PrototypePte
,
457 (ULONG_PTR
)Pfn1
->PteAddress
);
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT
,
469 (ULONG_PTR
)PointerPte
,
471 (ULONG_PTR
)Pfn1
->PteAddress
);
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1
->u2
.ShareCount
== 1);
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1
);
482 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
488 /* Destroy the PTE and flush the TLB */
489 MI_ERASE_PTE(PointerPte
);
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
496 IN ULONG_PTR EndingAddress
,
499 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
502 PEPROCESS CurrentProcess
;
504 BOOLEAN AddressGap
= FALSE
;
505 PSUBSECTION Subsection
;
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess
= PsGetCurrentProcess();
512 PointerPde
= MiAddressToPde(Va
);
513 PointerPte
= MiAddressToPte(Va
);
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
518 /* Don't worry about prototypes */
519 PrototypePte
= LastPrototypePte
= NULL
;
523 /* Get the prototype PTE */
524 PrototypePte
= Vad
->FirstPrototypePte
;
525 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
531 /* Loop the PTE for each VA */
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde
->u
.Long
)
537 /* There are gaps in the address space */
540 /* Still no valid PDE, try the next 4MB (or whatever) */
543 /* Update the PTE on this new boundary */
544 PointerPte
= MiPteToAddress(PointerPde
);
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
548 if (Va
> EndingAddress
) return;
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde
->u
.Hard
.Valid
)
554 /* It isn't, so map it in */
555 PointerPte
= MiPteToAddress(PointerPde
);
556 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
561 ASSERT(Va
<= EndingAddress
);
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap
) && (LastPrototypePte
))
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
574 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
578 /* No more subsections, we are done with prototype PTEs */
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
587 /* Capture the PDE and make sure it exists */
588 TempPte
= *PointerPte
;
591 MiDecrementPageTableReferences((PVOID
)Va
);
593 /* Check if the PTE is actually mapped in */
594 if (MI_IS_MAPPED_PTE(&TempPte
))
596 /* Are we dealing with section VAD? */
597 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
599 /* We need to skip to the next correct prototype PTE */
600 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
602 /* And we need the subsection to skip to the next last prototype PTE */
603 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
607 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
611 /* No more subsections, we are done with prototype PTEs */
616 /* Check for prototype PTE */
617 if ((TempPte
.u
.Hard
.Valid
== 0) &&
618 (TempPte
.u
.Soft
.Prototype
== 1))
621 MI_ERASE_PTE(PointerPte
);
625 /* Delete the PTE proper */
626 MiDeletePte(PointerPte
,
634 /* The PTE was never mapped, just nuke it here */
635 MI_ERASE_PTE(PointerPte
);
639 /* Update the address and PTE for it */
644 /* Making sure the PDE is still valid */
645 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
647 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
649 /* The PDE should still be valid at this point */
650 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
652 /* Check remaining PTE count (go back 1 page due to above loop) */
653 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
655 if (PointerPde
->u
.Long
!= 0)
657 /* Delete the PTE proper */
658 MiDeletePte(PointerPde
,
659 MiPteToAddress(PointerPde
),
665 /* Release the lock and get out if we're done */
666 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
667 if (Va
> EndingAddress
) return;
669 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
670 PointerPde
= MiAddressToPde(Va
);
676 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
677 OUT PBOOLEAN HaveBadAddress
,
678 OUT PULONG_PTR BadAddress
)
680 PEXCEPTION_RECORD ExceptionRecord
;
686 *HaveBadAddress
= FALSE
;
689 // Get the exception record
691 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
694 // Look at the exception code
696 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
697 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
698 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
701 // We can tell the address if we have more than one parameter
703 if (ExceptionRecord
->NumberParameters
> 1)
706 // Return the address
708 *HaveBadAddress
= TRUE
;
709 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
714 // Continue executing the next handler
716 return EXCEPTION_EXECUTE_HANDLER
;
721 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
722 IN PVOID SourceAddress
,
723 IN PEPROCESS TargetProcess
,
724 OUT PVOID TargetAddress
,
725 IN SIZE_T BufferSize
,
726 IN KPROCESSOR_MODE PreviousMode
,
727 OUT PSIZE_T ReturnSize
)
729 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
730 PMDL Mdl
= (PMDL
)MdlBuffer
;
731 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
732 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
733 volatile BOOLEAN PagesLocked
;
734 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
735 volatile PVOID MdlAddress
;
737 BOOLEAN HaveBadAddress
;
738 ULONG_PTR BadAddress
;
739 NTSTATUS Status
= STATUS_SUCCESS
;
743 // Calculate the maximum amount of data to move
745 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
746 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
747 CurrentSize
= TotalSize
;
748 RemainingSize
= BufferSize
;
751 // Loop as long as there is still data
753 while (RemainingSize
> 0)
756 // Check if this transfer will finish everything off
758 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
761 // Attach to the source address space
763 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
766 // Reset state for this pass
770 FailedInMoving
= FALSE
;
771 ASSERT(FailedInProbe
== FALSE
);
774 // Protect user-mode copy
779 // If this is our first time, probe the buffer
781 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
784 // Catch a failure here
786 FailedInProbe
= TRUE
;
791 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
796 FailedInProbe
= FALSE
;
800 // Initialize and probe and lock the MDL
802 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
803 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
809 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
818 // Use our SEH handler to pick this up
820 FailedInMapping
= TRUE
;
821 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
825 // Now let go of the source and grab to the target process
827 KeUnstackDetachProcess(&ApcState
);
828 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
831 // Check if this is our first time through
833 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
836 // Catch a failure here
838 FailedInProbe
= TRUE
;
843 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
848 FailedInProbe
= FALSE
;
852 // Now do the actual move
854 FailedInMoving
= TRUE
;
855 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
857 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
862 // Detach from whoever we may be attached to
864 KeUnstackDetachProcess(&ApcState
);
867 // Check if we had mapped the pages
869 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
872 // Check if we had locked the pages
874 if (PagesLocked
) MmUnlockPages(Mdl
);
877 // Check if we hit working set quota
879 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
884 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
888 // Check if we failed during the probe or mapping
890 if ((FailedInProbe
) || (FailedInMapping
))
895 Status
= _SEH2_GetExceptionCode();
896 _SEH2_YIELD(return Status
);
900 // Otherwise, we failed probably during the move
902 *ReturnSize
= BufferSize
- RemainingSize
;
906 // Check if we know exactly where we stopped copying
911 // Return the exact number of bytes copied
913 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
918 // Return partial copy
920 Status
= STATUS_PARTIAL_COPY
;
925 // Check for SEH status
927 if (Status
!= STATUS_SUCCESS
) return Status
;
930 // Detach from target
932 KeUnstackDetachProcess(&ApcState
);
937 MmUnmapLockedPages(MdlAddress
, Mdl
);
941 // Update location and size
943 RemainingSize
-= CurrentSize
;
944 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
945 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
951 *ReturnSize
= BufferSize
;
952 return STATUS_SUCCESS
;
957 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
958 IN PVOID SourceAddress
,
959 IN PEPROCESS TargetProcess
,
960 OUT PVOID TargetAddress
,
961 IN SIZE_T BufferSize
,
962 IN KPROCESSOR_MODE PreviousMode
,
963 OUT PSIZE_T ReturnSize
)
965 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
966 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
967 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
968 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
971 BOOLEAN HaveBadAddress
;
972 ULONG_PTR BadAddress
;
973 NTSTATUS Status
= STATUS_SUCCESS
;
977 // Calculate the maximum amount of data to move
979 TotalSize
= MI_MAX_TRANSFER_SIZE
;
980 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
981 CurrentSize
= TotalSize
;
982 RemainingSize
= BufferSize
;
985 // Check if we can use the stack
987 if (BufferSize
<= MI_POOL_COPY_BYTES
)
992 PoolAddress
= (PVOID
)StackBuffer
;
999 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1000 if (!PoolAddress
) ASSERT(FALSE
);
1001 HavePoolAddress
= TRUE
;
1005 // Loop as long as there is still data
1007 while (RemainingSize
> 0)
1010 // Check if this transfer will finish everything off
1012 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1015 // Attach to the source address space
1017 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1020 // Reset state for this pass
1022 FailedInMoving
= FALSE
;
1023 ASSERT(FailedInProbe
== FALSE
);
1026 // Protect user-mode copy
1031 // If this is our first time, probe the buffer
1033 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1036 // Catch a failure here
1038 FailedInProbe
= TRUE
;
1043 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1048 FailedInProbe
= FALSE
;
1054 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1057 // Now let go of the source and grab to the target process
1059 KeUnstackDetachProcess(&ApcState
);
1060 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1063 // Check if this is our first time through
1065 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1068 // Catch a failure here
1070 FailedInProbe
= TRUE
;
1075 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1080 FailedInProbe
= FALSE
;
1084 // Now do the actual move
1086 FailedInMoving
= TRUE
;
1087 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1089 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1094 // Detach from whoever we may be attached to
1096 KeUnstackDetachProcess(&ApcState
);
1099 // Check if we had allocated pool
1101 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1104 // Check if we failed during the probe
1111 Status
= _SEH2_GetExceptionCode();
1112 _SEH2_YIELD(return Status
);
1116 // Otherwise, we failed, probably during the move
1118 *ReturnSize
= BufferSize
- RemainingSize
;
1122 // Check if we know exactly where we stopped copying
1127 // Return the exact number of bytes copied
1129 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1134 // Return partial copy
1136 Status
= STATUS_PARTIAL_COPY
;
1141 // Check for SEH status
1143 if (Status
!= STATUS_SUCCESS
) return Status
;
1146 // Detach from target
1148 KeUnstackDetachProcess(&ApcState
);
1151 // Update location and size
1153 RemainingSize
-= CurrentSize
;
1154 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1155 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1160 // Check if we had allocated pool
1162 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1167 *ReturnSize
= BufferSize
;
1168 return STATUS_SUCCESS
;
1173 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1174 IN PVOID SourceAddress
,
1175 IN PEPROCESS TargetProcess
,
1176 OUT PVOID TargetAddress
,
1177 IN SIZE_T BufferSize
,
1178 IN KPROCESSOR_MODE PreviousMode
,
1179 OUT PSIZE_T ReturnSize
)
1182 PEPROCESS Process
= SourceProcess
;
1185 // Don't accept zero-sized buffers
1187 if (!BufferSize
) return STATUS_SUCCESS
;
1190 // If we are copying from ourselves, lock the target instead
1192 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1195 // Acquire rundown protection
1197 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1202 return STATUS_PROCESS_IS_TERMINATING
;
1206 // See if we should use the pool copy
1208 if (BufferSize
> MI_POOL_COPY_BYTES
)
1213 Status
= MiDoMappedCopy(SourceProcess
,
1226 Status
= MiDoPoolCopy(SourceProcess
,
1238 ExReleaseRundownProtection(&Process
->RundownProtect
);
1244 MmFlushVirtualMemory(IN PEPROCESS Process
,
1245 IN OUT PVOID
*BaseAddress
,
1246 IN OUT PSIZE_T RegionSize
,
1247 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1255 return STATUS_SUCCESS
;
1260 MiGetPageProtection(IN PMMPTE PointerPte
)
1264 PEPROCESS CurrentProcess
;
1265 PETHREAD CurrentThread
;
1266 BOOLEAN WsSafe
, WsShared
;
1271 /* Copy this PTE's contents */
1272 TempPte
= *PointerPte
;
1274 /* Assure it's not totally zero */
1275 ASSERT(TempPte
.u
.Long
);
1277 /* Check for a special prototype format */
1278 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1279 (TempPte
.u
.Soft
.Prototype
== 1))
1281 /* Check if the prototype PTE is not yet pointing to a PTE */
1282 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1284 /* The prototype PTE contains the protection */
1285 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1288 /* Get a pointer to the underlying shared PTE */
1289 PointerPte
= MiProtoPteToPte(&TempPte
);
1291 /* Since the PTE we want to read can be paged out at any time, we need
1292 to release the working set lock first, so that it can be paged in */
1293 CurrentThread
= PsGetCurrentThread();
1294 CurrentProcess
= PsGetCurrentProcess();
1295 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1300 /* Now read the PTE value */
1301 TempPte
= *PointerPte
;
1303 /* Check if that one is invalid */
1304 if (!TempPte
.u
.Hard
.Valid
)
1306 /* We get the protection directly from this PTE */
1307 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1311 /* The PTE is valid, so we might need to get the protection from
1312 the PFN. Lock the PFN database */
1313 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1315 /* Check if the PDE is still valid */
1316 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1318 /* It's not, make it valid */
1319 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1322 /* Now it's safe to read the PTE value again */
1323 TempPte
= *PointerPte
;
1324 ASSERT(TempPte
.u
.Long
!= 0);
1326 /* Check again if the PTE is invalid */
1327 if (!TempPte
.u
.Hard
.Valid
)
1329 /* The PTE is not valid, so we can use it's protection field */
1330 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1334 /* The PTE is valid, so we can find the protection in the
1335 OriginalPte field of the PFN */
1336 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1337 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1340 /* Release the PFN database */
1341 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1344 /* Lock the working set again */
1345 MiLockProcessWorkingSetForFault(CurrentProcess
,
1353 /* In the easy case of transition or demand zero PTE just return its protection */
1354 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1356 /* If we get here, the PTE is valid, so look up the page in PFN database */
1357 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1358 if (!Pfn
->u3
.e1
.PrototypePte
)
1360 /* Return protection of the original pte */
1361 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1362 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1365 /* This is software PTE */
1366 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1367 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1368 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1369 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1370 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1375 MiQueryAddressState(IN PVOID Va
,
1377 IN PEPROCESS TargetProcess
,
1378 OUT PULONG ReturnedProtect
,
1382 PMMPTE PointerPte
, ProtoPte
;
1384 #if (_MI_PAGING_LEVELS >= 3)
1387 #if (_MI_PAGING_LEVELS >= 4)
1390 MMPTE TempPte
, TempProtoPte
;
1391 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1392 ULONG State
= MEM_RESERVE
, Protect
= 0;
1393 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1394 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1396 /* Only normal VADs supported */
1397 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1399 /* Get the PDE and PTE for the address */
1400 PointerPde
= MiAddressToPde(Va
);
1401 PointerPte
= MiAddressToPte(Va
);
1402 #if (_MI_PAGING_LEVELS >= 3)
1403 PointerPpe
= MiAddressToPpe(Va
);
1405 #if (_MI_PAGING_LEVELS >= 4)
1406 PointerPxe
= MiAddressToPxe(Va
);
1409 /* Return the next range */
1410 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1414 #if (_MI_PAGING_LEVELS >= 4)
1415 /* Does the PXE exist? */
1416 if (PointerPxe
->u
.Long
== 0)
1418 /* It does not, next range starts at the next PXE */
1419 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1423 /* Is the PXE valid? */
1424 if (PointerPxe
->u
.Hard
.Valid
== 0)
1426 /* Is isn't, fault it in (make the PPE accessible) */
1427 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1430 #if (_MI_PAGING_LEVELS >= 3)
1431 /* Does the PPE exist? */
1432 if (PointerPpe
->u
.Long
== 0)
1434 /* It does not, next range starts at the next PPE */
1435 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1439 /* Is the PPE valid? */
1440 if (PointerPpe
->u
.Hard
.Valid
== 0)
1442 /* Is isn't, fault it in (make the PDE accessible) */
1443 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1447 /* Does the PDE exist? */
1448 if (PointerPde
->u
.Long
== 0)
1450 /* It does not, next range starts at the next PDE */
1451 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1455 /* Is the PDE valid? */
1456 if (PointerPde
->u
.Hard
.Valid
== 0)
1458 /* Is isn't, fault it in (make the PTE accessible) */
1459 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1462 /* We have a PTE that we can access now! */
1467 /* Is it safe to try reading the PTE? */
1470 /* FIXME: watch out for large pages */
1471 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1473 /* Capture the PTE */
1474 TempPte
= *PointerPte
;
1475 if (TempPte
.u
.Long
!= 0)
1477 /* The PTE is valid, so it's not zeroed out */
1478 DemandZeroPte
= FALSE
;
1480 /* Is it a decommited, invalid, or faulted PTE? */
1481 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1482 (TempPte
.u
.Hard
.Valid
== 0) &&
1483 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1484 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1486 /* Otherwise our defaults should hold */
1487 ASSERT(Protect
== 0);
1488 ASSERT(State
== MEM_RESERVE
);
1492 /* This means it's committed */
1495 /* We don't support these */
1496 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1497 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1498 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1500 /* Get protection state of this page */
1501 Protect
= MiGetPageProtection(PointerPte
);
1503 /* Check if this is an image-backed VAD */
1504 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1505 (TempPte
.u
.Soft
.Prototype
== 1) &&
1506 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1509 DPRINT1("Not supported\n");
1516 /* Check if this was a demand-zero PTE, since we need to find the state */
1519 /* Not yet handled */
1520 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1521 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1523 /* Check if this is private commited memory, or an section-backed VAD */
1524 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1526 /* Tell caller about the next range */
1527 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1529 /* Get the prototype PTE for this VAD */
1530 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1531 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1534 /* We should unlock the working set, but it's not being held! */
1536 /* Is the prototype PTE actually valid (committed)? */
1537 TempProtoPte
= *ProtoPte
;
1538 if (TempProtoPte
.u
.Long
)
1540 /* Unless this is a memory-mapped file, handle it like private VAD */
1542 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1543 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1546 /* We should re-lock the working set */
1549 else if (Vad
->u
.VadFlags
.MemCommit
)
1551 /* This is committed memory */
1554 /* Convert the protection */
1555 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1559 /* Return the protection code */
1560 *ReturnedProtect
= Protect
;
1566 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1567 IN PVOID BaseAddress
,
1568 OUT PVOID MemoryInformation
,
1569 IN SIZE_T MemoryInformationLength
,
1570 OUT PSIZE_T ReturnLength
)
1572 PEPROCESS TargetProcess
;
1573 NTSTATUS Status
= STATUS_SUCCESS
;
1575 PVOID Address
, NextAddress
;
1576 BOOLEAN Found
= FALSE
;
1577 ULONG NewProtect
, NewState
;
1579 MEMORY_BASIC_INFORMATION MemoryInfo
;
1580 KAPC_STATE ApcState
;
1581 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1582 PMEMORY_AREA MemoryArea
;
1583 SIZE_T ResultLength
;
1585 /* Check for illegal addresses in user-space, or the shared memory area */
1586 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1587 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1589 Address
= PAGE_ALIGN(BaseAddress
);
1591 /* Make up an info structure describing this range */
1592 MemoryInfo
.BaseAddress
= Address
;
1593 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1594 MemoryInfo
.Type
= MEM_PRIVATE
;
1596 /* Special case for shared data */
1597 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1599 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1600 MemoryInfo
.State
= MEM_COMMIT
;
1601 MemoryInfo
.Protect
= PAGE_READONLY
;
1602 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1606 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1607 MemoryInfo
.State
= MEM_RESERVE
;
1608 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1609 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1612 /* Return the data, NtQueryInformation already probed it*/
1613 if (PreviousMode
!= KernelMode
)
1617 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1618 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1620 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1622 Status
= _SEH2_GetExceptionCode();
1628 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1629 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1635 /* Check if this is for a local or remote process */
1636 if (ProcessHandle
== NtCurrentProcess())
1638 TargetProcess
= PsGetCurrentProcess();
1642 /* Reference the target process */
1643 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1644 PROCESS_QUERY_INFORMATION
,
1646 ExGetPreviousMode(),
1647 (PVOID
*)&TargetProcess
,
1649 if (!NT_SUCCESS(Status
)) return Status
;
1651 /* Attach to it now */
1652 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1655 /* Lock the address space and make sure the process isn't already dead */
1656 MmLockAddressSpace(&TargetProcess
->Vm
);
1657 if (TargetProcess
->VmDeleted
)
1659 /* Unlock the address space of the process */
1660 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1662 /* Check if we were attached */
1663 if (ProcessHandle
!= NtCurrentProcess())
1665 /* Detach and dereference the process */
1666 KeUnstackDetachProcess(&ApcState
);
1667 ObDereferenceObject(TargetProcess
);
1671 DPRINT1("Process is dying\n");
1672 return STATUS_PROCESS_IS_TERMINATING
;
1676 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1677 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1679 /* Scan on the right */
1680 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1681 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1684 /* Check if this VAD covers the allocation range */
1685 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1686 (BaseVpn
<= Vad
->EndingVpn
))
1693 /* Check if this VAD is too high */
1694 if (BaseVpn
< Vad
->StartingVpn
)
1696 /* Stop if there is no left child */
1697 if (!Vad
->LeftChild
) break;
1699 /* Search on the left next */
1700 Vad
= Vad
->LeftChild
;
1704 /* Then this VAD is too low, keep searching on the right */
1705 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1707 /* Stop if there is no right child */
1708 if (!Vad
->RightChild
) break;
1710 /* Search on the right next */
1711 Vad
= Vad
->RightChild
;
1716 /* Was a VAD found? */
1719 Address
= PAGE_ALIGN(BaseAddress
);
1721 /* Calculate region size */
1724 if (Vad
->StartingVpn
>= BaseVpn
)
1726 /* Region size is the free space till the start of that VAD */
1727 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1731 /* Get the next VAD */
1732 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1735 /* Region size is the free space till the start of that VAD */
1736 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1740 /* Maximum possible region size with that base address */
1741 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1747 /* Maximum possible region size with that base address */
1748 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1751 /* Unlock the address space of the process */
1752 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1754 /* Check if we were attached */
1755 if (ProcessHandle
!= NtCurrentProcess())
1757 /* Detach and derefernece the process */
1758 KeUnstackDetachProcess(&ApcState
);
1759 ObDereferenceObject(TargetProcess
);
1762 /* Build the rest of the initial information block */
1763 MemoryInfo
.BaseAddress
= Address
;
1764 MemoryInfo
.AllocationBase
= NULL
;
1765 MemoryInfo
.AllocationProtect
= 0;
1766 MemoryInfo
.State
= MEM_FREE
;
1767 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1768 MemoryInfo
.Type
= 0;
1770 /* Return the data, NtQueryInformation already probed it*/
1771 if (PreviousMode
!= KernelMode
)
1775 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1776 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1778 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1780 Status
= _SEH2_GetExceptionCode();
1786 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1787 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1793 /* Set the correct memory type based on what kind of VAD this is */
1794 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1795 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1797 MemoryInfo
.Type
= MEM_PRIVATE
;
1799 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1801 MemoryInfo
.Type
= MEM_IMAGE
;
1805 MemoryInfo
.Type
= MEM_MAPPED
;
1808 /* Find the memory area the specified address belongs to */
1809 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1810 ASSERT(MemoryArea
!= NULL
);
1812 /* Determine information dependent on the memory area type */
1813 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1815 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1816 if (!NT_SUCCESS(Status
))
1818 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1819 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1820 NT_ASSERT(NT_SUCCESS(Status
));
1825 /* Build the initial information block */
1826 Address
= PAGE_ALIGN(BaseAddress
);
1827 MemoryInfo
.BaseAddress
= Address
;
1828 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1829 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1830 MemoryInfo
.Type
= MEM_PRIVATE
;
1832 /* Acquire the working set lock (shared is enough) */
1833 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1835 /* Find the largest chunk of memory which has the same state and protection mask */
1836 MemoryInfo
.State
= MiQueryAddressState(Address
,
1839 &MemoryInfo
.Protect
,
1841 Address
= NextAddress
;
1842 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1844 /* Keep going unless the state or protection mask changed */
1845 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1846 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1847 Address
= NextAddress
;
1850 /* Release the working set lock */
1851 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1853 /* Check if we went outside of the VAD */
1854 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1856 /* Set the end of the VAD as the end address */
1857 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1860 /* Now that we know the last VA address, calculate the region size */
1861 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1864 /* Unlock the address space of the process */
1865 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1867 /* Check if we were attached */
1868 if (ProcessHandle
!= NtCurrentProcess())
1870 /* Detach and derefernece the process */
1871 KeUnstackDetachProcess(&ApcState
);
1872 ObDereferenceObject(TargetProcess
);
1875 /* Return the data, NtQueryInformation already probed it */
1876 if (PreviousMode
!= KernelMode
)
1880 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1881 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1883 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1885 Status
= _SEH2_GetExceptionCode();
1891 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1892 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1896 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1897 "State: %lx Type: %lx Size: %lx\n",
1898 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1899 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1900 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1907 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1908 IN ULONG_PTR EndingAddress
,
1910 IN PEPROCESS Process
)
1912 PMMPTE PointerPte
, LastPte
, PointerPde
;
1913 BOOLEAN OnBoundary
= TRUE
;
1916 /* Get the PDE and PTE addresses */
1917 PointerPde
= MiAddressToPde(StartingAddress
);
1918 PointerPte
= MiAddressToPte(StartingAddress
);
1919 LastPte
= MiAddressToPte(EndingAddress
);
1921 /* Loop all the PTEs */
1922 while (PointerPte
<= LastPte
)
1924 /* Check if we've hit an new PDE boundary */
1927 /* Is this PDE demand zero? */
1928 PointerPde
= MiAddressToPte(PointerPte
);
1929 if (PointerPde
->u
.Long
!= 0)
1931 /* It isn't -- is it valid? */
1932 if (PointerPde
->u
.Hard
.Valid
== 0)
1934 /* Nope, fault it in */
1935 PointerPte
= MiPteToAddress(PointerPde
);
1936 MiMakeSystemAddressValid(PointerPte
, Process
);
1941 /* The PTE was already valid, so move to the next one */
1943 PointerPte
= MiPteToAddress(PointerPde
);
1945 /* Is the entire VAD committed? If not, fail */
1946 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1948 /* Everything is committed so far past the range, return true */
1949 if (PointerPte
> LastPte
) return TRUE
;
1953 /* Is the PTE demand zero? */
1954 if (PointerPte
->u
.Long
== 0)
1956 /* Is the entire VAD committed? If not, fail */
1957 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1961 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1962 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1963 (PointerPte
->u
.Hard
.Valid
== 0) &&
1964 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1965 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1967 /* Then part of the range is decommitted, so fail */
1972 /* Move to the next PTE */
1974 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1977 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1983 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1984 IN OUT PVOID
*BaseAddress
,
1985 IN OUT PSIZE_T NumberOfBytesToProtect
,
1986 IN ULONG NewAccessProtection
,
1987 OUT PULONG OldAccessProtection OPTIONAL
)
1989 PMEMORY_AREA MemoryArea
;
1990 PMMSUPPORT AddressSpace
;
1991 ULONG OldAccessProtection_
;
1994 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1995 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1997 AddressSpace
= &Process
->Vm
;
1998 MmLockAddressSpace(AddressSpace
);
1999 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2000 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2002 MmUnlockAddressSpace(AddressSpace
);
2003 return STATUS_UNSUCCESSFUL
;
2006 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2008 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2009 Status
= MmProtectSectionView(AddressSpace
,
2012 *NumberOfBytesToProtect
,
2013 NewAccessProtection
,
2014 OldAccessProtection
);
2016 MmUnlockAddressSpace(AddressSpace
);
2023 MiProtectVirtualMemory(IN PEPROCESS Process
,
2024 IN OUT PVOID
*BaseAddress
,
2025 IN OUT PSIZE_T NumberOfBytesToProtect
,
2026 IN ULONG NewAccessProtection
,
2027 OUT PULONG OldAccessProtection OPTIONAL
)
2029 PMEMORY_AREA MemoryArea
;
2031 PMMSUPPORT AddressSpace
;
2032 ULONG_PTR StartingAddress
, EndingAddress
;
2033 PMMPTE PointerPde
, PointerPte
, LastPte
;
2036 ULONG ProtectionMask
, OldProtect
;
2038 NTSTATUS Status
= STATUS_SUCCESS
;
2039 PETHREAD Thread
= PsGetCurrentThread();
2040 TABLE_SEARCH_RESULT Result
;
2042 /* Calculate base address for the VAD */
2043 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2044 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2046 /* Calculate the protection mask and make sure it's valid */
2047 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2048 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2050 DPRINT1("Invalid protection mask\n");
2051 return STATUS_INVALID_PAGE_PROTECTION
;
2054 /* Check for ROS specific memory area */
2055 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2056 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
2059 return MiRosProtectVirtualMemory(Process
,
2061 NumberOfBytesToProtect
,
2062 NewAccessProtection
,
2063 OldAccessProtection
);
2066 /* Lock the address space and make sure the process isn't already dead */
2067 AddressSpace
= MmGetCurrentAddressSpace();
2068 MmLockAddressSpace(AddressSpace
);
2069 if (Process
->VmDeleted
)
2071 DPRINT1("Process is dying\n");
2072 Status
= STATUS_PROCESS_IS_TERMINATING
;
2076 /* Get the VAD for this address range, and make sure it exists */
2077 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2078 EndingAddress
>> PAGE_SHIFT
,
2080 (PMMADDRESS_NODE
*)&Vad
);
2081 if (Result
!= TableFoundNode
)
2083 DPRINT("Could not find a VAD for this allocation\n");
2084 Status
= STATUS_CONFLICTING_ADDRESSES
;
2088 /* Make sure the address is within this VAD's boundaries */
2089 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2090 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2092 Status
= STATUS_CONFLICTING_ADDRESSES
;
2096 /* These kinds of VADs are not supported atm */
2097 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2098 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2099 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2101 DPRINT1("Illegal VAD for attempting to set protection\n");
2102 Status
= STATUS_CONFLICTING_ADDRESSES
;
2106 /* Check for a VAD whose protection can't be changed */
2107 if (Vad
->u
.VadFlags
.NoChange
== 1)
2109 DPRINT1("Trying to change protection of a NoChange VAD\n");
2110 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2114 /* Is this section, or private memory? */
2115 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2117 /* Not yet supported */
2118 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2120 DPRINT1("Illegal VAD for attempting to set protection\n");
2121 Status
= STATUS_CONFLICTING_ADDRESSES
;
2125 /* Rotate VADs are not yet supported */
2126 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2128 DPRINT1("Illegal VAD for attempting to set protection\n");
2129 Status
= STATUS_CONFLICTING_ADDRESSES
;
2133 /* Not valid on section files */
2134 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2137 DPRINT1("Invalid protection flags for section\n");
2138 Status
= STATUS_INVALID_PARAMETER_4
;
2142 /* Check if data or page file mapping protection PTE is compatible */
2143 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2146 DPRINT1("Fixme: Not checking for valid protection\n");
2149 /* This is a section, and this is not yet supported */
2150 DPRINT1("Section protection not yet supported\n");
2155 /* Private memory, check protection flags */
2156 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2157 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2159 DPRINT1("Invalid protection flags for private memory\n");
2160 Status
= STATUS_INVALID_PARAMETER_4
;
2164 /* Lock the working set */
2165 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2167 /* Check if all pages in this range are committed */
2168 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2175 DPRINT1("The entire range is not committed\n");
2176 Status
= STATUS_NOT_COMMITTED
;
2177 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2181 /* Compute starting and ending PTE and PDE addresses */
2182 PointerPde
= MiAddressToPde(StartingAddress
);
2183 PointerPte
= MiAddressToPte(StartingAddress
);
2184 LastPte
= MiAddressToPte(EndingAddress
);
2186 /* Make this PDE valid */
2187 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2189 /* Save protection of the first page */
2190 if (PointerPte
->u
.Long
!= 0)
2192 /* Capture the page protection and make the PDE valid */
2193 OldProtect
= MiGetPageProtection(PointerPte
);
2194 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2198 /* Grab the old protection from the VAD itself */
2199 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2202 /* Loop all the PTEs now */
2203 while (PointerPte
<= LastPte
)
2205 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2206 if (MiIsPteOnPdeBoundary(PointerPte
))
2208 PointerPde
= MiAddressToPte(PointerPte
);
2209 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2212 /* Capture the PTE and check if it was empty */
2213 PteContents
= *PointerPte
;
2214 if (PteContents
.u
.Long
== 0)
2216 /* This used to be a zero PTE and it no longer is, so we must add a
2217 reference to the pagetable. */
2218 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2221 /* Check what kind of PTE we are dealing with */
2222 if (PteContents
.u
.Hard
.Valid
== 1)
2224 /* Get the PFN entry */
2225 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2227 /* We don't support this yet */
2228 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2230 /* Check if the page should not be accessible at all */
2231 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2232 (NewAccessProtection
& PAGE_GUARD
))
2234 /* The page should be in the WS and we should make it transition now */
2235 DPRINT1("Making valid page invalid is not yet supported!\n");
2236 Status
= STATUS_NOT_IMPLEMENTED
;
2237 /* Unlock the working set */
2238 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2242 /* Write the protection mask and write it with a TLB flush */
2243 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2244 MiFlushTbAndCapture(Vad
,
2252 /* We don't support these cases yet */
2253 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2254 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2256 /* The PTE is already demand-zero, just update the protection mask */
2257 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2258 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2259 ASSERT(PointerPte
->u
.Long
!= 0);
2262 /* Move to the next PTE */
2266 /* Unlock the working set */
2267 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2270 /* Unlock the address space */
2271 MmUnlockAddressSpace(AddressSpace
);
2273 /* Return parameters and success */
2274 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2275 *BaseAddress
= (PVOID
)StartingAddress
;
2276 *OldAccessProtection
= OldProtect
;
2277 return STATUS_SUCCESS
;
2280 /* Unlock the address space and return the failure code */
2281 MmUnlockAddressSpace(AddressSpace
);
2287 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2288 IN PEPROCESS TargetProcess
,
2291 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2294 // Sanity checks. The latter is because we only use this function with the
2295 // PFN lock not held, so it may go away in the future.
2297 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2298 ASSERT(OldIrql
== MM_NOIRQL
);
2301 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2302 // return the same address as the PDE on 2-level page table systems.
2304 // If everything is already valid, there is nothing to do.
2306 PointerPpe
= MiAddressToPte(PointerPde
);
2307 PointerPxe
= MiAddressToPde(PointerPde
);
2308 if ((PointerPxe
->u
.Hard
.Valid
) &&
2309 (PointerPpe
->u
.Hard
.Valid
) &&
2310 (PointerPde
->u
.Hard
.Valid
))
2316 // At least something is invalid, so begin by getting the PTE for the PDE itself
2317 // and then lookup each additional level. We must do it in this precise order
2318 // because the pagfault.c code (as well as in Windows) depends that the next
2319 // level up (higher) must be valid when faulting a lower level
2321 PointerPte
= MiPteToAddress(PointerPde
);
2325 // Make sure APCs continued to be disabled
2327 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2330 // First, make the PXE valid if needed
2332 if (!PointerPxe
->u
.Hard
.Valid
)
2334 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2335 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2341 if (!PointerPpe
->u
.Hard
.Valid
)
2343 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2344 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2348 // And finally, make the PDE itself valid.
2350 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2353 // This should've worked the first time so the loop is really just for
2354 // show -- ASSERT that we're actually NOT going to be looping.
2356 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2357 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2358 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2359 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2360 !(PointerPpe
->u
.Hard
.Valid
) ||
2361 !(PointerPde
->u
.Hard
.Valid
));
2366 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2372 PFN_NUMBER PageFrameIndex
;
2376 // Acquire the PFN lock and loop all the PTEs in the list
2378 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2379 for (i
= 0; i
!= Count
; i
++)
2382 // The PTE must currently be valid
2384 TempPte
= *ValidPteList
[i
];
2385 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2388 // Get the PFN entry for the page itself, and then for its page table
2390 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2391 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2392 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2395 // Decrement the share count on the page table, and then on the page
2398 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2399 MI_SET_PFN_DELETED(Pfn1
);
2400 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2403 // Make the page decommitted
2405 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2409 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2410 // and then release the PFN lock
2413 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2418 MiDecommitPages(IN PVOID StartingAddress
,
2419 IN PMMPTE EndingPte
,
2420 IN PEPROCESS Process
,
2423 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2424 ULONG CommitReduction
= 0;
2425 PMMPTE ValidPteList
[256];
2429 PETHREAD CurrentThread
= PsGetCurrentThread();
2432 // Get the PTE and PTE for the address, and lock the working set
2433 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2434 // commited range ends so that we can do the right accounting.
2436 PointerPde
= MiAddressToPde(StartingAddress
);
2437 PointerPte
= MiAddressToPte(StartingAddress
);
2438 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2439 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2442 // Make the PDE valid, and now loop through each page's worth of data
2444 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2445 while (PointerPte
<= EndingPte
)
2448 // Check if we've crossed a PDE boundary
2450 if (MiIsPteOnPdeBoundary(PointerPte
))
2453 // Get the new PDE and flush the valid PTEs we had built up until
2454 // now. This helps reduce the amount of TLB flushing we have to do.
2455 // Note that Windows does a much better job using timestamps and
2456 // such, and does not flush the entire TLB all the time, but right
2457 // now we have bigger problems to worry about than TLB flushing.
2459 PointerPde
= MiAddressToPde(StartingAddress
);
2462 MiProcessValidPteList(ValidPteList
, PteCount
);
2467 // Make this PDE valid
2469 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2473 // Read this PTE. It might be active or still demand-zero.
2475 PteContents
= *PointerPte
;
2476 if (PteContents
.u
.Long
)
2479 // The PTE is active. It might be valid and in a working set, or
2480 // it might be a prototype PTE or paged out or even in transition.
2482 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2485 // It's already decommited, so there's nothing for us to do here
2492 // Remove it from the counters, and check if it was valid or not
2494 //Process->NumberOfPrivatePages--;
2495 if (PteContents
.u
.Hard
.Valid
)
2498 // It's valid. At this point make sure that it is not a ROS
2499 // PFN. Also, we don't support ProtoPTEs in this code path.
2501 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2502 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2503 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2506 // Flush any pending PTEs that we had not yet flushed, if our
2507 // list has gotten too big, then add this PTE to the flush list.
2509 if (PteCount
== 256)
2511 MiProcessValidPteList(ValidPteList
, PteCount
);
2514 ValidPteList
[PteCount
++] = PointerPte
;
2519 // We do not support any of these other scenarios at the moment
2521 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2522 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2523 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2526 // So the only other possibility is that it is still a demand
2527 // zero PTE, in which case we undo the accounting we did
2528 // earlier and simply make the page decommitted.
2530 //Process->NumberOfPrivatePages++;
2531 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2538 // This used to be a zero PTE and it no longer is, so we must add a
2539 // reference to the pagetable.
2541 MiIncrementPageTableReferences(StartingAddress
);
2544 // Next, we account for decommitted PTEs and make the PTE as such
2546 if (PointerPte
> CommitPte
) CommitReduction
++;
2547 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2551 // Move to the next PTE and the next address
2554 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2558 // Flush any dangling PTEs from the loop in the last page table, and then
2559 // release the working set and return the commit reduction accounting.
2561 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2562 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2563 return CommitReduction
;
2566 /* PUBLIC FUNCTIONS ***********************************************************/
2573 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2584 MmSecureVirtualMemory(IN PVOID Address
,
2588 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2597 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2599 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2602 /* SYSTEM CALLS ***************************************************************/
2606 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2607 IN PVOID BaseAddress
,
2609 IN SIZE_T NumberOfBytesToRead
,
2610 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2612 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2614 NTSTATUS Status
= STATUS_SUCCESS
;
2615 SIZE_T BytesRead
= 0;
2619 // Check if we came from user mode
2621 if (PreviousMode
!= KernelMode
)
2624 // Validate the read addresses
2626 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2627 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2628 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2629 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2632 // Don't allow to write into kernel space
2634 return STATUS_ACCESS_VIOLATION
;
2638 // Enter SEH for probe
2643 // Probe the output value
2645 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2647 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2650 // Get exception code
2652 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2658 // Don't do zero-byte transfers
2660 if (NumberOfBytesToRead
)
2663 // Reference the process
2665 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2671 if (NT_SUCCESS(Status
))
2676 Status
= MmCopyVirtualMemory(Process
,
2678 PsGetCurrentProcess(),
2680 NumberOfBytesToRead
,
2685 // Dereference the process
2687 ObDereferenceObject(Process
);
2692 // Check if the caller sent this parameter
2694 if (NumberOfBytesRead
)
2697 // Enter SEH to guard write
2702 // Return the number of bytes read
2704 *NumberOfBytesRead
= BytesRead
;
2706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2720 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2721 IN PVOID BaseAddress
,
2723 IN SIZE_T NumberOfBytesToWrite
,
2724 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2726 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2728 NTSTATUS Status
= STATUS_SUCCESS
;
2729 SIZE_T BytesWritten
= 0;
2733 // Check if we came from user mode
2735 if (PreviousMode
!= KernelMode
)
2738 // Validate the read addresses
2740 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2741 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2742 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2743 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2746 // Don't allow to write into kernel space
2748 return STATUS_ACCESS_VIOLATION
;
2752 // Enter SEH for probe
2757 // Probe the output value
2759 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2761 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2764 // Get exception code
2766 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2772 // Don't do zero-byte transfers
2774 if (NumberOfBytesToWrite
)
2777 // Reference the process
2779 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2785 if (NT_SUCCESS(Status
))
2790 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2794 NumberOfBytesToWrite
,
2799 // Dereference the process
2801 ObDereferenceObject(Process
);
2806 // Check if the caller sent this parameter
2808 if (NumberOfBytesWritten
)
2811 // Enter SEH to guard write
2816 // Return the number of bytes written
2818 *NumberOfBytesWritten
= BytesWritten
;
2820 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2834 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2835 IN OUT PVOID
*UnsafeBaseAddress
,
2836 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2837 IN ULONG NewAccessProtection
,
2838 OUT PULONG UnsafeOldAccessProtection
)
2841 ULONG OldAccessProtection
;
2843 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2844 PVOID BaseAddress
= NULL
;
2845 SIZE_T NumberOfBytesToProtect
= 0;
2846 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2848 BOOLEAN Attached
= FALSE
;
2849 KAPC_STATE ApcState
;
2853 // Check for valid protection flags
2855 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2856 if (Protection
!= PAGE_NOACCESS
&&
2857 Protection
!= PAGE_READONLY
&&
2858 Protection
!= PAGE_READWRITE
&&
2859 Protection
!= PAGE_WRITECOPY
&&
2860 Protection
!= PAGE_EXECUTE
&&
2861 Protection
!= PAGE_EXECUTE_READ
&&
2862 Protection
!= PAGE_EXECUTE_READWRITE
&&
2863 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2868 return STATUS_INVALID_PAGE_PROTECTION
;
2872 // Check if we came from user mode
2874 if (PreviousMode
!= KernelMode
)
2877 // Enter SEH for probing
2882 // Validate all outputs
2884 ProbeForWritePointer(UnsafeBaseAddress
);
2885 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2886 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2891 BaseAddress
= *UnsafeBaseAddress
;
2892 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2894 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2897 // Get exception code
2899 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2908 BaseAddress
= *UnsafeBaseAddress
;
2909 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2913 // Catch illegal base address
2915 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2918 // Catch illegal region size
2920 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2925 return STATUS_INVALID_PARAMETER_3
;
2929 // 0 is also illegal
2931 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2934 // Get a reference to the process
2936 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2937 PROCESS_VM_OPERATION
,
2942 if (!NT_SUCCESS(Status
)) return Status
;
2945 // Check if we should attach
2947 if (CurrentProcess
!= Process
)
2952 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2957 // Do the actual work
2959 Status
= MiProtectVirtualMemory(Process
,
2961 &NumberOfBytesToProtect
,
2962 NewAccessProtection
,
2963 &OldAccessProtection
);
2968 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2971 // Release reference
2973 ObDereferenceObject(Process
);
2976 // Enter SEH to return data
2981 // Return data to user
2983 *UnsafeOldAccessProtection
= OldAccessProtection
;
2984 *UnsafeBaseAddress
= BaseAddress
;
2985 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2987 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3004 // HACK until we have proper WSLIST support
3005 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3007 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3009 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3021 // HACK until we have proper WSLIST support
3022 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3024 if (!Wsle
->u1
.e1
.LockedInWs
&&
3025 !Wsle
->u1
.e1
.LockedInMemory
)
3027 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3030 if (LockType
& MAP_PROCESS
)
3031 Wsle
->u1
.e1
.LockedInWs
= 1;
3032 if (LockType
& MAP_SYSTEM
)
3033 Wsle
->u1
.e1
.LockedInMemory
= 1;
3042 // HACK until we have proper WSLIST support
3043 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3045 if (LockType
& MAP_PROCESS
)
3046 Wsle
->u1
.e1
.LockedInWs
= 0;
3047 if (LockType
& MAP_SYSTEM
)
3048 Wsle
->u1
.e1
.LockedInMemory
= 0;
3050 if (!Wsle
->u1
.e1
.LockedInWs
&&
3051 !Wsle
->u1
.e1
.LockedInMemory
)
3053 MiDereferencePfnAndDropLockCount(Pfn1
);
3059 MiCheckVadsForLockOperation(
3060 _Inout_ PVOID
*BaseAddress
,
3061 _Inout_ PSIZE_T RegionSize
,
3062 _Inout_ PVOID
*EndAddress
)
3068 /* Get the base address and align the start address */
3069 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3070 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3071 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3073 /* First loop and check all VADs */
3074 CurrentVa
= *BaseAddress
;
3075 while (CurrentVa
< *EndAddress
)
3078 Vad
= MiLocateAddress(CurrentVa
);
3081 /// FIXME: this might be a memory area for a section view...
3082 return STATUS_ACCESS_VIOLATION
;
3085 /* Check VAD type */
3086 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3087 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3088 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3090 *EndAddress
= CurrentVa
;
3091 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3092 return STATUS_INCOMPATIBLE_FILE_MAP
;
3095 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3098 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3099 return STATUS_SUCCESS
;
3104 MiLockVirtualMemory(
3105 IN OUT PVOID
*BaseAddress
,
3106 IN OUT PSIZE_T RegionSize
,
3109 PEPROCESS CurrentProcess
;
3110 PMMSUPPORT AddressSpace
;
3111 PVOID CurrentVa
, EndAddress
;
3112 PMMPTE PointerPte
, LastPte
;
3114 #if (_MI_PAGING_LEVELS >= 3)
3117 #if (_MI_PAGING_LEVELS == 4)
3121 NTSTATUS Status
, TempStatus
;
3123 /* Lock the address space */
3124 AddressSpace
= MmGetCurrentAddressSpace();
3125 MmLockAddressSpace(AddressSpace
);
3127 /* Make sure we still have an address space */
3128 CurrentProcess
= PsGetCurrentProcess();
3129 if (CurrentProcess
->VmDeleted
)
3131 Status
= STATUS_PROCESS_IS_TERMINATING
;
3135 /* Check the VADs in the requested range */
3136 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3137 if (!NT_SUCCESS(Status
))
3142 /* Enter SEH for probing */
3145 /* Loop all pages and probe them */
3146 CurrentVa
= *BaseAddress
;
3147 while (CurrentVa
< EndAddress
)
3149 (void)(*(volatile CHAR
*)CurrentVa
);
3150 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3153 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3155 Status
= _SEH2_GetExceptionCode();
3160 /* All pages were accessible, since we hold the address space lock, nothing
3161 can be de-committed. Assume success for now. */
3162 Status
= STATUS_SUCCESS
;
3164 /* Get the PTE and PDE */
3165 PointerPte
= MiAddressToPte(*BaseAddress
);
3166 PointerPde
= MiAddressToPde(*BaseAddress
);
3167 #if (_MI_PAGING_LEVELS >= 3)
3168 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3170 #if (_MI_PAGING_LEVELS == 4)
3171 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3174 /* Get the last PTE */
3175 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3177 /* Lock the process working set */
3178 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3180 /* Loop the pages */
3183 /* Check for a page that is not accessible */
3185 #if (_MI_PAGING_LEVELS == 4)
3186 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3188 #if (_MI_PAGING_LEVELS >= 3)
3189 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3191 (PointerPde
->u
.Hard
.Valid
== 0) ||
3192 (PointerPte
->u
.Hard
.Valid
== 0))
3194 /* Release process working set */
3195 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3197 /* Access the page */
3198 CurrentVa
= MiPteToAddress(PointerPte
);
3200 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3201 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3202 if (!NT_SUCCESS(TempStatus
))
3204 // This should only happen, when remote backing storage is not accessible
3206 Status
= TempStatus
;
3210 /* Lock the process working set */
3211 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3215 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3216 ASSERT(Pfn1
!= NULL
);
3218 /* Check the previous lock status */
3219 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3221 Status
= STATUS_WAS_LOCKED
;
3225 MI_LOCK_VA(Pfn1
, MapType
);
3227 /* Go to the next PTE */
3230 /* Check if we're on a PDE boundary */
3231 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3232 #if (_MI_PAGING_LEVELS >= 3)
3233 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3235 #if (_MI_PAGING_LEVELS == 4)
3236 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3238 } while (PointerPte
<= LastPte
);
3240 /* Release process working set */
3241 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3244 /* Unlock address space */
3245 MmUnlockAddressSpace(AddressSpace
);
3252 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3253 IN OUT PVOID
*BaseAddress
,
3254 IN OUT PSIZE_T NumberOfBytesToLock
,
3258 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3260 BOOLEAN Attached
= FALSE
;
3261 KAPC_STATE ApcState
;
3262 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3263 PVOID CapturedBaseAddress
;
3264 SIZE_T CapturedBytesToLock
;
3270 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3273 // Invalid set of flags
3275 return STATUS_INVALID_PARAMETER
;
3279 // At least one flag must be specified
3281 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3286 return STATUS_INVALID_PARAMETER
;
3290 // Enter SEH for probing
3295 // Validate output data
3297 ProbeForWritePointer(BaseAddress
);
3298 ProbeForWriteSize_t(NumberOfBytesToLock
);
3303 CapturedBaseAddress
= *BaseAddress
;
3304 CapturedBytesToLock
= *NumberOfBytesToLock
;
3306 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3309 // Get exception code
3311 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3316 // Catch illegal base address
3318 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3321 // Catch illegal region size
3323 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3328 return STATUS_INVALID_PARAMETER
;
3332 // 0 is also illegal
3334 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3337 // Get a reference to the process
3339 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3340 PROCESS_VM_OPERATION
,
3345 if (!NT_SUCCESS(Status
)) return Status
;
3348 // Check if this is is system-mapped
3350 if (MapType
& MAP_SYSTEM
)
3353 // Check for required privilege
3355 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3358 // Fail: Don't have it
3360 ObDereferenceObject(Process
);
3361 return STATUS_PRIVILEGE_NOT_HELD
;
3366 // Check if we should attach
3368 if (CurrentProcess
!= Process
)
3373 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3378 // Call the internal function
3380 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3381 &CapturedBytesToLock
,
3387 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3390 // Release reference
3392 ObDereferenceObject(Process
);
3395 // Enter SEH to return data
3400 // Return data to user
3402 *BaseAddress
= CapturedBaseAddress
;
3403 *NumberOfBytesToLock
= CapturedBytesToLock
;
3405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3408 // Get exception code
3410 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3423 MiUnlockVirtualMemory(
3424 IN OUT PVOID
*BaseAddress
,
3425 IN OUT PSIZE_T RegionSize
,
3428 PEPROCESS CurrentProcess
;
3429 PMMSUPPORT AddressSpace
;
3431 PMMPTE PointerPte
, LastPte
;
3433 #if (_MI_PAGING_LEVELS >= 3)
3436 #if (_MI_PAGING_LEVELS == 4)
3442 /* Lock the address space */
3443 AddressSpace
= MmGetCurrentAddressSpace();
3444 MmLockAddressSpace(AddressSpace
);
3446 /* Make sure we still have an address space */
3447 CurrentProcess
= PsGetCurrentProcess();
3448 if (CurrentProcess
->VmDeleted
)
3450 Status
= STATUS_PROCESS_IS_TERMINATING
;
3454 /* Check the VADs in the requested range */
3455 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3457 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3458 incompatible VAD we continue, like Windows does */
3459 if (Status
== STATUS_ACCESS_VIOLATION
)
3461 Status
= STATUS_NOT_LOCKED
;
3465 /* Get the PTE and PDE */
3466 PointerPte
= MiAddressToPte(*BaseAddress
);
3467 PointerPde
= MiAddressToPde(*BaseAddress
);
3468 #if (_MI_PAGING_LEVELS >= 3)
3469 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3471 #if (_MI_PAGING_LEVELS == 4)
3472 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3475 /* Get the last PTE */
3476 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3478 /* Lock the process working set */
3479 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3481 /* Loop the pages */
3484 /* Check for a page that is not present */
3486 #if (_MI_PAGING_LEVELS == 4)
3487 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3489 #if (_MI_PAGING_LEVELS >= 3)
3490 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3492 (PointerPde
->u
.Hard
.Valid
== 0) ||
3493 (PointerPte
->u
.Hard
.Valid
== 0))
3495 /* Remember it, but keep going */
3496 Status
= STATUS_NOT_LOCKED
;
3501 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3502 ASSERT(Pfn1
!= NULL
);
3504 /* Check if all of the requested locks are present */
3505 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3506 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3508 /* Remember it, but keep going */
3509 Status
= STATUS_NOT_LOCKED
;
3511 /* Check if no lock is present */
3512 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3514 DPRINT1("FIXME: Should remove the page from WS\n");
3519 /* Go to the next PTE */
3522 /* Check if we're on a PDE boundary */
3523 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3524 #if (_MI_PAGING_LEVELS >= 3)
3525 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3527 #if (_MI_PAGING_LEVELS == 4)
3528 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3530 } while (PointerPte
<= LastPte
);
3532 /* Check if we hit a page that was not locked */
3533 if (Status
== STATUS_NOT_LOCKED
)
3535 goto CleanupWithWsLock
;
3538 /* All pages in the region were locked, so unlock them all */
3540 /* Get the PTE and PDE */
3541 PointerPte
= MiAddressToPte(*BaseAddress
);
3542 PointerPde
= MiAddressToPde(*BaseAddress
);
3543 #if (_MI_PAGING_LEVELS >= 3)
3544 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3546 #if (_MI_PAGING_LEVELS == 4)
3547 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3550 /* Loop the pages */
3554 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3555 MI_UNLOCK_VA(Pfn1
, MapType
);
3557 /* Go to the next PTE */
3560 /* Check if we're on a PDE boundary */
3561 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3562 #if (_MI_PAGING_LEVELS >= 3)
3563 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3565 #if (_MI_PAGING_LEVELS == 4)
3566 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3568 } while (PointerPte
<= LastPte
);
3570 /* Everything is done */
3571 Status
= STATUS_SUCCESS
;
3575 /* Release process working set */
3576 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3579 /* Unlock address space */
3580 MmUnlockAddressSpace(AddressSpace
);
3588 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3589 IN OUT PVOID
*BaseAddress
,
3590 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3594 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3596 BOOLEAN Attached
= FALSE
;
3597 KAPC_STATE ApcState
;
3598 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3599 PVOID CapturedBaseAddress
;
3600 SIZE_T CapturedBytesToUnlock
;
3606 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3609 // Invalid set of flags
3611 return STATUS_INVALID_PARAMETER
;
3615 // At least one flag must be specified
3617 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3622 return STATUS_INVALID_PARAMETER
;
3626 // Enter SEH for probing
3631 // Validate output data
3633 ProbeForWritePointer(BaseAddress
);
3634 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3639 CapturedBaseAddress
= *BaseAddress
;
3640 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3645 // Get exception code
3647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3652 // Catch illegal base address
3654 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3657 // Catch illegal region size
3659 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3664 return STATUS_INVALID_PARAMETER
;
3668 // 0 is also illegal
3670 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3673 // Get a reference to the process
3675 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3676 PROCESS_VM_OPERATION
,
3681 if (!NT_SUCCESS(Status
)) return Status
;
3684 // Check if this is is system-mapped
3686 if (MapType
& MAP_SYSTEM
)
3689 // Check for required privilege
3691 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3694 // Fail: Don't have it
3696 ObDereferenceObject(Process
);
3697 return STATUS_PRIVILEGE_NOT_HELD
;
3702 // Check if we should attach
3704 if (CurrentProcess
!= Process
)
3709 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3714 // Call the internal function
3716 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3717 &CapturedBytesToUnlock
,
3723 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3726 // Release reference
3728 ObDereferenceObject(Process
);
3731 // Enter SEH to return data
3736 // Return data to user
3738 *BaseAddress
= CapturedBaseAddress
;
3739 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3741 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3744 // Get exception code
3746 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3753 return STATUS_SUCCESS
;
3758 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3759 IN OUT PVOID
*BaseAddress
,
3760 IN OUT PSIZE_T NumberOfBytesToFlush
,
3761 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3765 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3766 PVOID CapturedBaseAddress
;
3767 SIZE_T CapturedBytesToFlush
;
3768 IO_STATUS_BLOCK LocalStatusBlock
;
3772 // Check if we came from user mode
3774 if (PreviousMode
!= KernelMode
)
3777 // Enter SEH for probing
3782 // Validate all outputs
3784 ProbeForWritePointer(BaseAddress
);
3785 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3786 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3791 CapturedBaseAddress
= *BaseAddress
;
3792 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3797 // Get exception code
3799 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3808 CapturedBaseAddress
= *BaseAddress
;
3809 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3813 // Catch illegal base address
3815 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3818 // Catch illegal region size
3820 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3825 return STATUS_INVALID_PARAMETER
;
3829 // Get a reference to the process
3831 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3832 PROCESS_VM_OPERATION
,
3837 if (!NT_SUCCESS(Status
)) return Status
;
3842 Status
= MmFlushVirtualMemory(Process
,
3843 &CapturedBaseAddress
,
3844 &CapturedBytesToFlush
,
3848 // Release reference
3850 ObDereferenceObject(Process
);
3853 // Enter SEH to return data
3858 // Return data to user
3860 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3861 *NumberOfBytesToFlush
= 0;
3862 *IoStatusBlock
= LocalStatusBlock
;
3864 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3880 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3882 IN PVOID BaseAddress
,
3883 IN SIZE_T RegionSize
,
3884 IN PVOID
*UserAddressArray
,
3885 OUT PULONG_PTR EntriesInUserAddressArray
,
3886 OUT PULONG Granularity
)
3891 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3892 ULONG_PTR CapturedEntryCount
;
3896 // Check if we came from user mode
3898 if (PreviousMode
!= KernelMode
)
3901 // Enter SEH for probing
3906 // Catch illegal base address
3908 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3911 // Catch illegal region size
3913 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3918 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
3922 // Validate all data
3924 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3925 ProbeForWriteUlong(Granularity
);
3930 CapturedEntryCount
= *EntriesInUserAddressArray
;
3933 // Must have a count
3935 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3938 // Can't be larger than the maximum
3940 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3945 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3949 // Probe the actual array
3951 ProbeForWrite(UserAddressArray
,
3952 CapturedEntryCount
* sizeof(PVOID
),
3955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3958 // Get exception code
3960 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3969 CapturedEntryCount
= *EntriesInUserAddressArray
;
3970 ASSERT(CapturedEntryCount
!= 0);
3974 // Check if this is a local request
3976 if (ProcessHandle
== NtCurrentProcess())
3979 // No need to reference the process
3981 Process
= PsGetCurrentProcess();
3986 // Reference the target
3988 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3989 PROCESS_VM_OPERATION
,
3994 if (!NT_SUCCESS(Status
)) return Status
;
3998 // Compute the last address and validate it
4000 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4001 if (BaseAddress
> EndAddress
)
4006 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4007 return STATUS_INVALID_PARAMETER_4
;
4016 // Dereference if needed
4018 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4021 // Enter SEH to return data
4026 // Return data to user
4028 *EntriesInUserAddressArray
= 0;
4029 *Granularity
= PAGE_SIZE
;
4031 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4034 // Get exception code
4036 Status
= _SEH2_GetExceptionCode();
4043 return STATUS_SUCCESS
;
4051 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4052 IN PVOID BaseAddress
,
4053 IN SIZE_T RegionSize
)
4058 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4059 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4062 // Catch illegal base address
4064 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4067 // Catch illegal region size
4069 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4074 return STATUS_INVALID_PARAMETER_3
;
4078 // Check if this is a local request
4080 if (ProcessHandle
== NtCurrentProcess())
4083 // No need to reference the process
4085 Process
= PsGetCurrentProcess();
4090 // Reference the target
4092 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4093 PROCESS_VM_OPERATION
,
4098 if (!NT_SUCCESS(Status
)) return Status
;
4102 // Compute the last address and validate it
4104 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4105 if (BaseAddress
> EndAddress
)
4110 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4111 return STATUS_INVALID_PARAMETER_3
;
4120 // Dereference if needed
4122 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4127 return STATUS_SUCCESS
;
4132 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4133 IN PVOID BaseAddress
,
4134 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4135 OUT PVOID MemoryInformation
,
4136 IN SIZE_T MemoryInformationLength
,
4137 OUT PSIZE_T ReturnLength
)
4139 NTSTATUS Status
= STATUS_SUCCESS
;
4140 KPROCESSOR_MODE PreviousMode
;
4142 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4144 /* Bail out if the address is invalid */
4145 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4147 /* Probe return buffer */
4148 PreviousMode
= ExGetPreviousMode();
4149 if (PreviousMode
!= KernelMode
)
4153 ProbeForWrite(MemoryInformation
,
4154 MemoryInformationLength
,
4157 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4159 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4161 Status
= _SEH2_GetExceptionCode();
4165 if (!NT_SUCCESS(Status
))
4171 switch(MemoryInformationClass
)
4173 case MemoryBasicInformation
:
4174 /* Validate the size information of the class */
4175 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4177 /* The size is invalid */
4178 return STATUS_INFO_LENGTH_MISMATCH
;
4180 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4183 MemoryInformationLength
,
4187 case MemorySectionName
:
4188 /* Validate the size information of the class */
4189 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4191 /* The size is invalid */
4192 return STATUS_INFO_LENGTH_MISMATCH
;
4194 Status
= MiQueryMemorySectionName(ProcessHandle
,
4197 MemoryInformationLength
,
4200 case MemoryWorkingSetList
:
4201 case MemoryBasicVlmInformation
:
4203 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4215 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4216 IN OUT PVOID
* UBaseAddress
,
4217 IN ULONG_PTR ZeroBits
,
4218 IN OUT PSIZE_T URegionSize
,
4219 IN ULONG AllocationType
,
4223 PMEMORY_AREA MemoryArea
;
4224 PFN_NUMBER PageCount
;
4225 PMMVAD Vad
= NULL
, FoundVad
;
4227 PMMSUPPORT AddressSpace
;
4229 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
, HighestAddress
;
4230 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4231 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4232 PETHREAD CurrentThread
= PsGetCurrentThread();
4233 KAPC_STATE ApcState
;
4234 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4235 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4237 PMMPTE PointerPte
, PointerPde
, LastPte
;
4238 TABLE_SEARCH_RESULT Result
;
4239 PMMADDRESS_NODE Parent
;
4242 /* Check for valid Zero bits */
4243 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4245 DPRINT1("Too many zero bits\n");
4246 return STATUS_INVALID_PARAMETER_3
;
4249 /* Check for valid Allocation Types */
4250 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4251 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4253 DPRINT1("Invalid Allocation Type\n");
4254 return STATUS_INVALID_PARAMETER_5
;
4257 /* Check for at least one of these Allocation Types to be set */
4258 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4260 DPRINT1("No memory allocation base type\n");
4261 return STATUS_INVALID_PARAMETER_5
;
4264 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4265 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4267 DPRINT1("Invalid use of MEM_RESET\n");
4268 return STATUS_INVALID_PARAMETER_5
;
4271 /* Check if large pages are being used */
4272 if (AllocationType
& MEM_LARGE_PAGES
)
4274 /* Large page allocations MUST be committed */
4275 if (!(AllocationType
& MEM_COMMIT
))
4277 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4278 return STATUS_INVALID_PARAMETER_5
;
4281 /* These flags are not allowed with large page allocations */
4282 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4284 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4285 return STATUS_INVALID_PARAMETER_5
;
4289 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4290 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4292 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4293 return STATUS_INVALID_PARAMETER_5
;
4296 /* Check for valid MEM_PHYSICAL usage */
4297 if (AllocationType
& MEM_PHYSICAL
)
4299 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4300 if (!(AllocationType
& MEM_RESERVE
))
4302 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4303 return STATUS_INVALID_PARAMETER_5
;
4306 /* Only these flags are allowed with MEM_PHYSIAL */
4307 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4309 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4310 return STATUS_INVALID_PARAMETER_5
;
4313 /* Then make sure PAGE_READWRITE is used */
4314 if (Protect
!= PAGE_READWRITE
)
4316 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4317 return STATUS_INVALID_PARAMETER_6
;
4321 /* Calculate the protection mask and make sure it's valid */
4322 ProtectionMask
= MiMakeProtectionMask(Protect
);
4323 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4325 DPRINT1("Invalid protection mask\n");
4326 return STATUS_INVALID_PAGE_PROTECTION
;
4332 /* Check for user-mode parameters */
4333 if (PreviousMode
!= KernelMode
)
4335 /* Make sure they are writable */
4336 ProbeForWritePointer(UBaseAddress
);
4337 ProbeForWriteSize_t(URegionSize
);
4340 /* Capture their values */
4341 PBaseAddress
= *UBaseAddress
;
4342 PRegionSize
= *URegionSize
;
4344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4346 /* Return the exception code */
4347 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4351 /* Make sure the allocation isn't past the VAD area */
4352 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4354 DPRINT1("Virtual allocation base above User Space\n");
4355 return STATUS_INVALID_PARAMETER_2
;
4358 /* Make sure the allocation wouldn't overflow past the VAD area */
4359 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4361 DPRINT1("Region size would overflow into kernel-memory\n");
4362 return STATUS_INVALID_PARAMETER_4
;
4365 /* Make sure there's a size specified */
4368 DPRINT1("Region size is invalid (zero)\n");
4369 return STATUS_INVALID_PARAMETER_4
;
4373 // If this is for the current process, just use PsGetCurrentProcess
4375 if (ProcessHandle
== NtCurrentProcess())
4377 Process
= CurrentProcess
;
4382 // Otherwise, reference the process with VM rights and attach to it if
4383 // this isn't the current process. We must attach because we'll be touching
4384 // PTEs and PDEs that belong to user-mode memory, and also touching the
4385 // Working Set which is stored in Hyperspace.
4387 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4388 PROCESS_VM_OPERATION
,
4393 if (!NT_SUCCESS(Status
)) return Status
;
4394 if (CurrentProcess
!= Process
)
4396 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4402 // Check for large page allocations and make sure that the required privilege
4403 // is being held, before attempting to handle them.
4405 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4406 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4408 /* Fail without it */
4409 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4410 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4411 goto FailPathNoLock
;
4415 // Fail on the things we don't yet support
4417 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4419 DPRINT1("MEM_LARGE_PAGES not supported\n");
4420 Status
= STATUS_INVALID_PARAMETER
;
4421 goto FailPathNoLock
;
4423 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4425 DPRINT1("MEM_PHYSICAL not supported\n");
4426 Status
= STATUS_INVALID_PARAMETER
;
4427 goto FailPathNoLock
;
4429 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4431 DPRINT1("MEM_WRITE_WATCH not supported\n");
4432 Status
= STATUS_INVALID_PARAMETER
;
4433 goto FailPathNoLock
;
4437 // Check if the caller is reserving memory, or committing memory and letting
4438 // us pick the base address
4440 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4443 // Do not allow COPY_ON_WRITE through this API
4445 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4447 DPRINT1("Copy on write not allowed through this path\n");
4448 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4449 goto FailPathNoLock
;
4453 // Does the caller have an address in mind, or is this a blind commit?
4458 // This is a blind commit, all we need is the region size
4460 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4461 PageCount
= BYTES_TO_PAGES(PRegionSize
);
4463 StartingAddress
= 0;
4466 // Check if ZeroBits were specified
4471 // Calculate the highest address and check if it's valid
4473 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4474 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4476 Status
= STATUS_INVALID_PARAMETER_3
;
4477 goto FailPathNoLock
;
4483 // Use the highest VAD address as maximum
4485 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4491 // This is a reservation, so compute the starting address on the
4492 // expected 64KB granularity, and see where the ending address will
4493 // fall based on the aligned address and the passed in region size
4495 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4496 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4497 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
4501 // Allocate and initialize the VAD
4503 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4506 DPRINT1("Failed to allocate a VAD!\n");
4507 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4508 goto FailPathNoLock
;
4511 Vad
->u
.LongFlags
= 0;
4512 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4513 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4514 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4515 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
4518 // Lock the address space and make sure the process isn't already dead
4520 AddressSpace
= MmGetCurrentAddressSpace();
4521 MmLockAddressSpace(AddressSpace
);
4522 if (Process
->VmDeleted
)
4524 Status
= STATUS_PROCESS_IS_TERMINATING
;
4529 // Did we have a base address? If no, find a valid address that is 64KB
4530 // aligned in the VAD tree. Otherwise, make sure that the address range
4531 // which was passed in isn't already conflicting with an existing address
4536 /* Which way should we search? */
4537 if ((AllocationType
& MEM_TOP_DOWN
) || Process
->VmTopDown
)
4539 /* Find an address top-down */
4540 Result
= MiFindEmptyAddressRangeDownTree(PRegionSize
,
4549 /* Find an address bottom-up */
4550 Result
= MiFindEmptyAddressRangeInTree(PRegionSize
,
4557 if (Result
== TableFoundNode
)
4559 Status
= STATUS_NO_MEMORY
;
4564 // Now we know where the allocation ends. Make sure it doesn't end up
4565 // somewhere in kernel mode.
4567 ASSERT(StartingAddress
!= 0);
4568 ASSERT(StartingAddress
< (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
);
4569 EndingAddress
= (StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4570 ASSERT(EndingAddress
> StartingAddress
);
4571 if (EndingAddress
> HighestAddress
)
4573 Status
= STATUS_NO_MEMORY
;
4579 /* Make sure it doesn't conflict with an existing allocation */
4580 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4581 EndingAddress
>> PAGE_SHIFT
,
4584 if (Result
== TableFoundNode
)
4587 // The address specified is in conflict!
4589 Status
= STATUS_CONFLICTING_ADDRESSES
;
4595 // Write out the VAD fields for this allocation
4597 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
4598 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
4601 // FIXME: Should setup VAD bitmap
4603 Status
= STATUS_SUCCESS
;
4606 // Lock the working set and insert the VAD into the process VAD tree
4608 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4609 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4610 Process
->VadRoot
.NodeHint
= Vad
;
4611 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
4612 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4615 // Make sure the actual region size is at least as big as the
4616 // requested region size and update the value
4618 ASSERT(PRegionSize
<= (EndingAddress
+ 1 - StartingAddress
));
4619 PRegionSize
= (EndingAddress
+ 1 - StartingAddress
);
4622 // Update the virtual size of the process, and if this is now the highest
4623 // virtual size we have ever seen, update the peak virtual size to reflect
4626 Process
->VirtualSize
+= PRegionSize
;
4627 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4629 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4633 // Release address space and detach and dereference the target process if
4634 // it was different from the current process
4636 MmUnlockAddressSpace(AddressSpace
);
4637 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4638 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4641 // Use SEH to write back the base address and the region size. In the case
4642 // of an exception, we do not return back the exception code, as the memory
4643 // *has* been allocated. The caller would now have to call VirtualQuery
4644 // or do some other similar trick to actually find out where its memory
4645 // allocation ended up
4649 *URegionSize
= PRegionSize
;
4650 *UBaseAddress
= (PVOID
)StartingAddress
;
4652 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4655 // Ignore exception!
4659 return STATUS_SUCCESS
;
4663 // This is a MEM_COMMIT on top of an existing address which must have been
4664 // MEM_RESERVED already. Compute the start and ending base addresses based
4665 // on the user input, and then compute the actual region size once all the
4666 // alignments have been done.
4668 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4669 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4670 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4673 // Lock the address space and make sure the process isn't already dead
4675 AddressSpace
= MmGetCurrentAddressSpace();
4676 MmLockAddressSpace(AddressSpace
);
4677 if (Process
->VmDeleted
)
4679 DPRINT1("Process is dying\n");
4680 Status
= STATUS_PROCESS_IS_TERMINATING
;
4685 // Get the VAD for this address range, and make sure it exists
4687 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4688 EndingAddress
>> PAGE_SHIFT
,
4690 (PMMADDRESS_NODE
*)&FoundVad
);
4691 if (Result
!= TableFoundNode
)
4693 DPRINT1("Could not find a VAD for this allocation\n");
4694 Status
= STATUS_CONFLICTING_ADDRESSES
;
4698 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4700 /// @todo HACK: pretend success
4701 DPRINT("MEM_RESET not supported\n");
4702 Status
= STATUS_SUCCESS
;
4707 // These kinds of VADs are illegal for this Windows function when trying to
4708 // commit an existing range
4710 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4711 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4712 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4714 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4715 Status
= STATUS_CONFLICTING_ADDRESSES
;
4720 // Make sure that this address range actually fits within the VAD for it
4722 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4723 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4725 DPRINT1("Address range does not fit into the VAD\n");
4726 Status
= STATUS_CONFLICTING_ADDRESSES
;
4731 // Make sure this is an ARM3 section
4733 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4734 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4736 DPRINT1("Illegal commit of non-ARM3 section!\n");
4737 Status
= STATUS_ALREADY_COMMITTED
;
4741 // Is this a previously reserved section being committed? If so, enter the
4742 // special section path
4744 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4747 // You cannot commit large page sections through this API
4749 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4751 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4752 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4757 // You can only use caching flags on a rotate VAD
4759 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4760 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4762 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4763 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4768 // We should make sure that the section's permissions aren't being
4771 if (FoundVad
->u
.VadFlags
.NoChange
)
4774 // Make sure it's okay to touch it
4775 // Note: The Windows 2003 kernel has a bug here, passing the
4776 // unaligned base address together with the aligned size,
4777 // potentially covering a region larger than the actual allocation.
4778 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4779 // For now we keep this behavior.
4780 // TODO: analyze possible implications, create test case
4782 Status
= MiCheckSecuredVad(FoundVad
,
4786 if (!NT_SUCCESS(Status
))
4788 DPRINT1("Secured VAD being messed around with\n");
4794 // ARM3 does not support file-backed sections, only shared memory
4796 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4799 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4801 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4802 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4804 DPRINT1("Invalid page protection for rotate VAD\n");
4805 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4810 // Compute PTE addresses and the quota charge, then grab the commit lock
4812 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4813 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4814 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4815 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4818 // Get the segment template PTE and start looping each page
4820 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4821 ASSERT(TempPte
.u
.Long
!= 0);
4822 while (PointerPte
<= LastPte
)
4825 // For each non-already-committed page, write the invalid template PTE
4827 if (PointerPte
->u
.Long
== 0)
4829 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4839 // Now do the commit accounting and release the lock
4841 ASSERT(QuotaCharge
>= QuotaFree
);
4842 QuotaCharge
-= QuotaFree
;
4843 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4844 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4847 // We are done with committing the section pages
4849 Status
= STATUS_SUCCESS
;
4854 // This is a specific ReactOS check because we only use normal VADs
4856 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4859 // While this is an actual Windows check
4861 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4864 // Throw out attempts to use copy-on-write through this API path
4866 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4868 DPRINT1("Write copy attempted when not allowed\n");
4869 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4874 // Initialize a demand-zero PTE
4877 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4878 NT_ASSERT(TempPte
.u
.Long
!= 0);
4881 // Get the PTE, PDE and the last PTE for this address range
4883 PointerPde
= MiAddressToPde(StartingAddress
);
4884 PointerPte
= MiAddressToPte(StartingAddress
);
4885 LastPte
= MiAddressToPte(EndingAddress
);
4888 // Update the commit charge in the VAD as well as in the process, and check
4889 // if this commit charge was now higher than the last recorded peak, in which
4890 // case we also update the peak
4892 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4893 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4894 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4896 Process
->CommitChargePeak
= Process
->CommitCharge
;
4900 // Lock the working set while we play with user pages and page tables
4902 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4905 // Make the current page table valid, and then loop each page within it
4907 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4908 while (PointerPte
<= LastPte
)
4911 // Have we crossed into a new page table?
4913 if (MiIsPteOnPdeBoundary(PointerPte
))
4916 // Get the PDE and now make it valid too
4918 PointerPde
= MiAddressToPte(PointerPte
);
4919 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4923 // Is this a zero PTE as expected?
4925 if (PointerPte
->u
.Long
== 0)
4928 // First increment the count of pages in the page table for this
4931 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4934 // And now write the invalid demand-zero PTE as requested
4936 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4938 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4941 // If the PTE was already decommitted, there is nothing else to do
4942 // but to write the new demand-zero PTE
4944 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4946 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4949 // We don't handle these scenarios yet
4951 if (PointerPte
->u
.Soft
.Valid
== 0)
4953 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4954 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4958 // There's a change in protection, remember this for later, but do
4959 // not yet handle it.
4961 ChangeProtection
= TRUE
;
4965 // Move to the next PTE
4971 // Release the working set lock, unlock the address space, and detach from
4972 // the target process if it was not the current process. Also dereference the
4973 // target process if this wasn't the case.
4975 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4976 Status
= STATUS_SUCCESS
;
4978 MmUnlockAddressSpace(AddressSpace
);
4980 if (!NT_SUCCESS(Status
))
4984 ExFreePoolWithTag(Vad
, 'SdaV');
4989 // Check if we need to update the protection
4991 if (ChangeProtection
)
4993 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
4994 SIZE_T ProtectSize
= PRegionSize
;
4995 ULONG OldProtection
;
4998 // Change the protection of the region
5000 MiProtectVirtualMemory(Process
,
5001 &ProtectBaseAddress
,
5008 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5009 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5012 // Only write back results on success
5014 if (NT_SUCCESS(Status
))
5017 // Use SEH to write back the base address and the region size. In the case
5018 // of an exception, we strangely do return back the exception code, even
5019 // though the memory *has* been allocated. This mimics Windows behavior and
5020 // there is not much we can do about it.
5024 *URegionSize
= PRegionSize
;
5025 *UBaseAddress
= (PVOID
)StartingAddress
;
5027 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5029 Status
= _SEH2_GetExceptionCode();
5042 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5043 IN PVOID
* UBaseAddress
,
5044 IN PSIZE_T URegionSize
,
5047 PMEMORY_AREA MemoryArea
;
5050 LONG_PTR CommitReduction
= 0;
5051 ULONG_PTR StartingAddress
, EndingAddress
;
5055 PMMSUPPORT AddressSpace
;
5056 PETHREAD CurrentThread
= PsGetCurrentThread();
5057 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5058 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5059 KAPC_STATE ApcState
;
5060 BOOLEAN Attached
= FALSE
;
5064 // Only two flags are supported
5066 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5068 DPRINT1("Invalid FreeType\n");
5069 return STATUS_INVALID_PARAMETER_4
;
5073 // Check if no flag was used, or if both flags were used
5075 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5076 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5078 DPRINT1("Invalid FreeType combination\n");
5079 return STATUS_INVALID_PARAMETER_4
;
5083 // Enter SEH for probe and capture. On failure, return back to the caller
5084 // with an exception violation.
5089 // Check for user-mode parameters and make sure that they are writeable
5091 if (PreviousMode
!= KernelMode
)
5093 ProbeForWritePointer(UBaseAddress
);
5094 ProbeForWriteUlong(URegionSize
);
5098 // Capture the current values
5100 PBaseAddress
= *UBaseAddress
;
5101 PRegionSize
= *URegionSize
;
5103 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5105 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5110 // Make sure the allocation isn't past the user area
5112 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5114 DPRINT1("Virtual free base above User Space\n");
5115 return STATUS_INVALID_PARAMETER_2
;
5119 // Make sure the allocation wouldn't overflow past the user area
5121 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5123 DPRINT1("Region size would overflow into kernel-memory\n");
5124 return STATUS_INVALID_PARAMETER_3
;
5128 // If this is for the current process, just use PsGetCurrentProcess
5130 if (ProcessHandle
== NtCurrentProcess())
5132 Process
= CurrentProcess
;
5137 // Otherwise, reference the process with VM rights and attach to it if
5138 // this isn't the current process. We must attach because we'll be touching
5139 // PTEs and PDEs that belong to user-mode memory, and also touching the
5140 // Working Set which is stored in Hyperspace.
5142 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5143 PROCESS_VM_OPERATION
,
5148 if (!NT_SUCCESS(Status
)) return Status
;
5149 if (CurrentProcess
!= Process
)
5151 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5157 // Lock the address space
5159 AddressSpace
= MmGetCurrentAddressSpace();
5160 MmLockAddressSpace(AddressSpace
);
5163 // If the address space is being deleted, fail the de-allocation since it's
5164 // too late to do anything about it
5166 if (Process
->VmDeleted
)
5168 DPRINT1("Process is dead\n");
5169 Status
= STATUS_PROCESS_IS_TERMINATING
;
5174 // Compute start and end addresses, and locate the VAD
5176 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5177 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5178 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5181 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5182 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5187 // If the range exceeds the VAD's ending VPN, fail this request
5189 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5191 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5192 Status
= STATUS_UNABLE_TO_FREE_VM
;
5197 // Only private memory (except rotate VADs) can be freed through here */
5199 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5200 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5201 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5203 DPRINT1("Attempt to free section memory\n");
5204 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5209 // ARM3 does not yet handle protected VM
5211 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5214 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5215 // and that is is an ARM3 memory area, and not a section view, as we currently
5216 // don't support freeing those though this interface.
5218 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5220 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5223 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5225 if (FreeType
& MEM_RELEASE
)
5228 // ARM3 only supports this VAD in this path
5230 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5233 // Is the caller trying to remove the whole VAD, or remove only a portion
5234 // of it? If no region size is specified, then the assumption is that the
5235 // whole VAD is to be destroyed
5240 // The caller must specify the base address identically to the range
5241 // that is stored in the VAD.
5243 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5245 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5246 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5251 // Now compute the actual start/end addresses based on the VAD
5253 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5254 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5257 // Finally lock the working set and remove the VAD from the VAD tree
5259 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5260 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5261 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5266 // This means the caller wants to release a specific region within
5267 // the range. We have to find out which range this is -- the following
5268 // possibilities exist plus their union (CASE D):
5270 // STARTING ADDRESS ENDING ADDRESS
5271 // [<========][========================================][=========>]
5272 // CASE A CASE B CASE C
5275 // First, check for case A or D
5277 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5282 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5285 // This is the easiest one to handle -- it is identical to
5286 // the code path above when the caller sets a zero region size
5287 // and the whole VAD is destroyed
5289 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5290 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5291 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5296 // This case is pretty easy too -- we compute a bunch of
5297 // pages to decommit, and then push the VAD's starting address
5298 // a bit further down, then decrement the commit charge
5300 // NOT YET IMPLEMENTED IN ARM3.
5302 DPRINT1("Case A not handled\n");
5303 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5307 // After analyzing the VAD, set it to NULL so that we don't
5308 // free it in the exit path
5316 // This is case B or case C. First check for case C
5318 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5320 PMEMORY_AREA MemoryArea
;
5323 // This is pretty easy and similar to case A. We compute the
5324 // amount of pages to decommit, update the VAD's commit charge
5325 // and then change the ending address of the VAD to be a bit
5328 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5329 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5333 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5334 // For ReactOS: shrink the corresponding memory area
5335 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5336 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
5337 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
5338 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
5339 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
5344 // This is case B and the hardest one. Because we are removing
5345 // a chunk of memory from the very middle of the VAD, we must
5346 // actually split the VAD into two new VADs and compute the
5347 // commit charges for each of them, and reinsert new charges.
5349 // NOT YET IMPLEMENTED IN ARM3.
5351 DPRINT1("Case B not handled\n");
5352 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5357 // After analyzing the VAD, set it to NULL so that we don't
5358 // free it in the exit path
5365 // Now we have a range of pages to dereference, so call the right API
5366 // to do that and then release the working set, since we're done messing
5367 // around with process pages.
5369 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5370 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5371 Status
= STATUS_SUCCESS
;
5375 // Update the process counters
5377 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5378 Process
->CommitCharge
-= CommitReduction
;
5379 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5382 // Unlock the address space and free the VAD in failure cases. Next,
5383 // detach from the target process so we can write the region size and the
5384 // base address to the correct source process, and dereference the target
5387 MmUnlockAddressSpace(AddressSpace
);
5388 if (Vad
) ExFreePool(Vad
);
5389 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5390 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5393 // Use SEH to safely return the region size and the base address of the
5394 // deallocation. If we get an access violation, don't return a failure code
5395 // as the deallocation *has* happened. The caller will just have to figure
5396 // out another way to find out where it is (such as VirtualQuery).
5400 *URegionSize
= PRegionSize
;
5401 *UBaseAddress
= (PVOID
)StartingAddress
;
5403 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5411 // This is the decommit path. You cannot decommit from the following VADs in
5412 // Windows, so fail the vall
5414 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5415 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5416 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5418 DPRINT1("Trying to decommit from invalid VAD\n");
5419 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5424 // If the caller did not specify a region size, first make sure that this
5425 // region is actually committed. If it is, then compute the ending address
5426 // based on the VAD.
5430 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5432 DPRINT1("Decomitting non-committed memory\n");
5433 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5436 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5440 // Decommit the PTEs for the range plus the actual backing pages for the
5441 // range, then reduce that amount from the commit charge in the VAD
5443 CommitReduction
= MiAddressToPte(EndingAddress
) -
5444 MiAddressToPte(StartingAddress
) +
5446 MiDecommitPages((PVOID
)StartingAddress
,
5447 MiAddressToPte(EndingAddress
),
5450 ASSERT(CommitReduction
>= 0);
5451 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5452 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5455 // We are done, go to the exit path without freeing the VAD as it remains
5456 // valid since we have not released the allocation.
5459 Status
= STATUS_SUCCESS
;
5463 // In the failure path, we detach and derefernece the target process, and
5464 // return whatever failure code was sent.
5467 MmUnlockAddressSpace(AddressSpace
);
5468 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5469 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5476 MmGetPhysicalAddress(PVOID Address
)
5478 PHYSICAL_ADDRESS PhysicalAddress
;
5482 /* Check if the PXE/PPE/PDE is valid */
5484 #if (_MI_PAGING_LEVELS == 4)
5485 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5487 #if (_MI_PAGING_LEVELS >= 3)
5488 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5490 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5492 /* Check for large pages */
5493 TempPde
= *MiAddressToPde(Address
);
5494 if (TempPde
.u
.Hard
.LargePage
)
5496 /* Physical address is base page + large page offset */
5497 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5498 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5499 return PhysicalAddress
;
5502 /* Check if the PTE is valid */
5503 TempPte
= *MiAddressToPte(Address
);
5504 if (TempPte
.u
.Hard
.Valid
)
5506 /* Physical address is base page + page offset */
5507 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5508 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5509 return PhysicalAddress
;
5513 KeRosDumpStackFrames(NULL
, 20);
5514 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5515 PhysicalAddress
.QuadPart
= 0;
5516 return PhysicalAddress
;