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
= 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 PointerPte
->u
.Long
= 0;
356 /* Actual legitimate pages */
362 * The only other ARM3 possibility is a demand zero page, which would
363 * mean freeing some of the paged pool pages that haven't even been
364 * touched yet, as part of a larger allocation.
366 * Right now, we shouldn't expect any page file information in the PTE
368 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
370 /* Destroy the PTE */
371 PointerPte
->u
.Long
= 0;
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 PointerPte
->u
.Long
= 0;
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
;
506 PUSHORT UsedPageTableEntries
;
508 /* Get out if this is a fake VAD, RosMm will free the marea pages */
509 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
511 /* Grab the process and PTE/PDE for the address being deleted */
512 CurrentProcess
= PsGetCurrentProcess();
513 PointerPde
= MiAddressToPde(Va
);
514 PointerPte
= MiAddressToPte(Va
);
516 /* Check if this is a section VAD or a VM VAD */
517 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
519 /* Don't worry about prototypes */
520 PrototypePte
= LastPrototypePte
= NULL
;
524 /* Get the prototype PTE */
525 PrototypePte
= Vad
->FirstPrototypePte
;
526 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
529 /* In all cases, we don't support fork() yet */
530 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
532 /* Loop the PTE for each VA */
535 /* First keep going until we find a valid PDE */
536 while (!PointerPde
->u
.Long
)
538 /* There are gaps in the address space */
541 /* Still no valid PDE, try the next 4MB (or whatever) */
544 /* Update the PTE on this new boundary */
545 PointerPte
= MiPteToAddress(PointerPde
);
547 /* Check if all the PDEs are invalid, so there's nothing to free */
548 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
549 if (Va
> EndingAddress
) return;
552 /* Now check if the PDE is mapped in */
553 if (!PointerPde
->u
.Hard
.Valid
)
555 /* It isn't, so map it in */
556 PointerPte
= MiPteToAddress(PointerPde
);
557 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
560 /* Now we should have a valid PDE, mapped in, and still have some VA */
561 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
562 ASSERT(Va
<= EndingAddress
);
563 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
565 /* Check if this is a section VAD with gaps in it */
566 if ((AddressGap
) && (LastPrototypePte
))
568 /* We need to skip to the next correct prototype PTE */
569 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
571 /* And we need the subsection to skip to the next last prototype PTE */
572 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
576 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
580 /* No more subsections, we are done with prototype PTEs */
585 /* Lock the PFN Database while we delete the PTEs */
586 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
589 /* Capture the PDE and make sure it exists */
590 TempPte
= *PointerPte
;
593 *UsedPageTableEntries
-= 1;
594 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
596 /* Check if the PTE is actually mapped in */
597 if (TempPte
.u
.Long
& 0xFFFFFC01)
599 /* Are we dealing with section VAD? */
600 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
602 /* We need to skip to the next correct prototype PTE */
603 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
605 /* And we need the subsection to skip to the next last prototype PTE */
606 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
610 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
614 /* No more subsections, we are done with prototype PTEs */
619 /* Check for prototype PTE */
620 if ((TempPte
.u
.Hard
.Valid
== 0) &&
621 (TempPte
.u
.Soft
.Prototype
== 1))
624 PointerPte
->u
.Long
= 0;
628 /* Delete the PTE proper */
629 MiDeletePte(PointerPte
,
637 /* The PTE was never mapped, just nuke it here */
638 PointerPte
->u
.Long
= 0;
642 /* Update the address and PTE for it */
647 /* Making sure the PDE is still valid */
648 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
650 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
652 /* The PDE should still be valid at this point */
653 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
655 if (*UsedPageTableEntries
== 0)
657 if (PointerPde
->u
.Long
!= 0)
659 /* Delete the PTE proper */
660 MiDeletePte(PointerPde
,
661 MiPteToAddress(PointerPde
),
667 /* Release the lock and get out if we're done */
668 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
669 if (Va
> EndingAddress
) return;
671 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
672 PointerPde
= MiAddressToPde(Va
);
678 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
679 OUT PBOOLEAN HaveBadAddress
,
680 OUT PULONG_PTR BadAddress
)
682 PEXCEPTION_RECORD ExceptionRecord
;
688 *HaveBadAddress
= FALSE
;
691 // Get the exception record
693 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
696 // Look at the exception code
698 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
699 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
700 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
703 // We can tell the address if we have more than one parameter
705 if (ExceptionRecord
->NumberParameters
> 1)
708 // Return the address
710 *HaveBadAddress
= TRUE
;
711 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
716 // Continue executing the next handler
718 return EXCEPTION_EXECUTE_HANDLER
;
723 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
724 IN PVOID SourceAddress
,
725 IN PEPROCESS TargetProcess
,
726 OUT PVOID TargetAddress
,
727 IN SIZE_T BufferSize
,
728 IN KPROCESSOR_MODE PreviousMode
,
729 OUT PSIZE_T ReturnSize
)
731 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
732 PMDL Mdl
= (PMDL
)MdlBuffer
;
733 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
734 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
735 volatile BOOLEAN PagesLocked
;
736 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
737 volatile PVOID MdlAddress
;
739 BOOLEAN HaveBadAddress
;
740 ULONG_PTR BadAddress
;
741 NTSTATUS Status
= STATUS_SUCCESS
;
745 // Calculate the maximum amount of data to move
747 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
748 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
749 CurrentSize
= TotalSize
;
750 RemainingSize
= BufferSize
;
753 // Loop as long as there is still data
755 while (RemainingSize
> 0)
758 // Check if this transfer will finish everything off
760 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
763 // Attach to the source address space
765 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
768 // Reset state for this pass
772 FailedInMoving
= FALSE
;
773 ASSERT(FailedInProbe
== FALSE
);
776 // Protect user-mode copy
781 // If this is our first time, probe the buffer
783 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
786 // Catch a failure here
788 FailedInProbe
= TRUE
;
793 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
798 FailedInProbe
= FALSE
;
802 // Initialize and probe and lock the MDL
804 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
805 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
811 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
820 // Use our SEH handler to pick this up
822 FailedInMapping
= TRUE
;
823 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
827 // Now let go of the source and grab to the target process
829 KeUnstackDetachProcess(&ApcState
);
830 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
833 // Check if this is our first time through
835 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
838 // Catch a failure here
840 FailedInProbe
= TRUE
;
845 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
850 FailedInProbe
= FALSE
;
854 // Now do the actual move
856 FailedInMoving
= TRUE
;
857 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
859 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
864 // Detach from whoever we may be attached to
866 KeUnstackDetachProcess(&ApcState
);
869 // Check if we had mapped the pages
871 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
874 // Check if we had locked the pages
876 if (PagesLocked
) MmUnlockPages(Mdl
);
879 // Check if we hit working set quota
881 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
886 return STATUS_WORKING_SET_QUOTA
;
890 // Check if we failed during the probe or mapping
892 if ((FailedInProbe
) || (FailedInMapping
))
897 Status
= _SEH2_GetExceptionCode();
898 _SEH2_YIELD(return Status
);
902 // Otherwise, we failed probably during the move
904 *ReturnSize
= BufferSize
- RemainingSize
;
908 // Check if we know exactly where we stopped copying
913 // Return the exact number of bytes copied
915 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
920 // Return partial copy
922 Status
= STATUS_PARTIAL_COPY
;
927 // Check for SEH status
929 if (Status
!= STATUS_SUCCESS
) return Status
;
932 // Detach from target
934 KeUnstackDetachProcess(&ApcState
);
939 MmUnmapLockedPages(MdlAddress
, Mdl
);
943 // Update location and size
945 RemainingSize
-= CurrentSize
;
946 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
947 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
953 *ReturnSize
= BufferSize
;
954 return STATUS_SUCCESS
;
959 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
960 IN PVOID SourceAddress
,
961 IN PEPROCESS TargetProcess
,
962 OUT PVOID TargetAddress
,
963 IN SIZE_T BufferSize
,
964 IN KPROCESSOR_MODE PreviousMode
,
965 OUT PSIZE_T ReturnSize
)
967 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
968 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
969 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
970 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
973 BOOLEAN HaveBadAddress
;
974 ULONG_PTR BadAddress
;
975 NTSTATUS Status
= STATUS_SUCCESS
;
979 // Calculate the maximum amount of data to move
981 TotalSize
= MI_MAX_TRANSFER_SIZE
;
982 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
983 CurrentSize
= TotalSize
;
984 RemainingSize
= BufferSize
;
987 // Check if we can use the stack
989 if (BufferSize
<= MI_POOL_COPY_BYTES
)
994 PoolAddress
= (PVOID
)StackBuffer
;
1001 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1002 if (!PoolAddress
) ASSERT(FALSE
);
1003 HavePoolAddress
= TRUE
;
1007 // Loop as long as there is still data
1009 while (RemainingSize
> 0)
1012 // Check if this transfer will finish everything off
1014 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1017 // Attach to the source address space
1019 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1022 // Reset state for this pass
1024 FailedInMoving
= FALSE
;
1025 ASSERT(FailedInProbe
== FALSE
);
1028 // Protect user-mode copy
1033 // If this is our first time, probe the buffer
1035 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1038 // Catch a failure here
1040 FailedInProbe
= TRUE
;
1045 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1050 FailedInProbe
= FALSE
;
1056 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1059 // Now let go of the source and grab to the target process
1061 KeUnstackDetachProcess(&ApcState
);
1062 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1065 // Check if this is our first time through
1067 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1070 // Catch a failure here
1072 FailedInProbe
= TRUE
;
1077 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1082 FailedInProbe
= FALSE
;
1086 // Now do the actual move
1088 FailedInMoving
= TRUE
;
1089 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1091 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1096 // Detach from whoever we may be attached to
1098 KeUnstackDetachProcess(&ApcState
);
1101 // Check if we had allocated pool
1103 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1106 // Check if we failed during the probe
1113 Status
= _SEH2_GetExceptionCode();
1114 _SEH2_YIELD(return Status
);
1118 // Otherwise, we failed, probably during the move
1120 *ReturnSize
= BufferSize
- RemainingSize
;
1124 // Check if we know exactly where we stopped copying
1129 // Return the exact number of bytes copied
1131 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1136 // Return partial copy
1138 Status
= STATUS_PARTIAL_COPY
;
1143 // Check for SEH status
1145 if (Status
!= STATUS_SUCCESS
) return Status
;
1148 // Detach from target
1150 KeUnstackDetachProcess(&ApcState
);
1153 // Update location and size
1155 RemainingSize
-= CurrentSize
;
1156 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1157 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1162 // Check if we had allocated pool
1164 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1169 *ReturnSize
= BufferSize
;
1170 return STATUS_SUCCESS
;
1175 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1176 IN PVOID SourceAddress
,
1177 IN PEPROCESS TargetProcess
,
1178 OUT PVOID TargetAddress
,
1179 IN SIZE_T BufferSize
,
1180 IN KPROCESSOR_MODE PreviousMode
,
1181 OUT PSIZE_T ReturnSize
)
1184 PEPROCESS Process
= SourceProcess
;
1187 // Don't accept zero-sized buffers
1189 if (!BufferSize
) return STATUS_SUCCESS
;
1192 // If we are copying from ourselves, lock the target instead
1194 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1197 // Acquire rundown protection
1199 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1204 return STATUS_PROCESS_IS_TERMINATING
;
1208 // See if we should use the pool copy
1210 if (BufferSize
> MI_POOL_COPY_BYTES
)
1215 Status
= MiDoMappedCopy(SourceProcess
,
1228 Status
= MiDoPoolCopy(SourceProcess
,
1240 ExReleaseRundownProtection(&Process
->RundownProtect
);
1246 MmFlushVirtualMemory(IN PEPROCESS Process
,
1247 IN OUT PVOID
*BaseAddress
,
1248 IN OUT PSIZE_T RegionSize
,
1249 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1257 return STATUS_SUCCESS
;
1262 MiGetPageProtection(IN PMMPTE PointerPte
)
1268 /* Copy this PTE's contents */
1269 TempPte
= *PointerPte
;
1271 /* Assure it's not totally zero */
1272 ASSERT(TempPte
.u
.Long
);
1274 /* Check for a special prototype format */
1275 if (TempPte
.u
.Soft
.Valid
== 0 &&
1276 TempPte
.u
.Soft
.Prototype
== 1)
1278 /* Unsupported now */
1283 /* In the easy case of transition or demand zero PTE just return its protection */
1284 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1286 /* If we get here, the PTE is valid, so look up the page in PFN database */
1287 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1288 if (!Pfn
->u3
.e1
.PrototypePte
)
1290 /* Return protection of the original pte */
1291 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1292 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1295 /* This is software PTE */
1296 DPRINT1("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1297 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte
));
1298 DPRINT1("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1299 DPRINT1("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1300 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1305 MiQueryAddressState(IN PVOID Va
,
1307 IN PEPROCESS TargetProcess
,
1308 OUT PULONG ReturnedProtect
,
1312 PMMPTE PointerPte
, ProtoPte
;
1314 MMPTE TempPte
, TempProtoPte
;
1315 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1316 ULONG State
= MEM_RESERVE
, Protect
= 0;
1317 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1318 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1320 /* Only normal VADs supported */
1321 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1323 /* Get the PDE and PTE for the address */
1324 PointerPde
= MiAddressToPde(Va
);
1325 PointerPte
= MiAddressToPte(Va
);
1327 /* Return the next range */
1328 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1330 /* Is the PDE demand-zero? */
1331 if (PointerPde
->u
.Long
!= 0)
1333 /* It is not. Is it valid? */
1334 if (PointerPde
->u
.Hard
.Valid
== 0)
1336 /* Is isn't, fault it in */
1337 PointerPte
= MiPteToAddress(PointerPde
);
1338 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1344 /* It is, skip it and move to the next PDE */
1345 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1348 /* Is it safe to try reading the PTE? */
1351 /* FIXME: watch out for large pages */
1352 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1354 /* Capture the PTE */
1355 TempPte
= *PointerPte
;
1356 if (TempPte
.u
.Long
!= 0)
1358 /* The PTE is valid, so it's not zeroed out */
1359 DemandZeroPte
= FALSE
;
1361 /* Is it a decommited, invalid, or faulted PTE? */
1362 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1363 (TempPte
.u
.Hard
.Valid
== 0) &&
1364 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1365 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1367 /* Otherwise our defaults should hold */
1368 ASSERT(Protect
== 0);
1369 ASSERT(State
== MEM_RESERVE
);
1373 /* This means it's committed */
1376 /* We don't support these */
1377 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1378 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1379 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1381 /* Get protection state of this page */
1382 Protect
= MiGetPageProtection(PointerPte
);
1384 /* Check if this is an image-backed VAD */
1385 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1386 (TempPte
.u
.Soft
.Prototype
== 1) &&
1387 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1390 DPRINT1("Not supported\n");
1397 /* Check if this was a demand-zero PTE, since we need to find the state */
1400 /* Not yet handled */
1401 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1402 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1404 /* Check if this is private commited memory, or an section-backed VAD */
1405 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1407 /* Tell caller about the next range */
1408 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1410 /* Get the prototype PTE for this VAD */
1411 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1412 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1415 /* We should unlock the working set, but it's not being held! */
1417 /* Is the prototype PTE actually valid (committed)? */
1418 TempProtoPte
= *ProtoPte
;
1419 if (TempProtoPte
.u
.Long
)
1421 /* Unless this is a memory-mapped file, handle it like private VAD */
1423 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1424 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1427 /* We should re-lock the working set */
1430 else if (Vad
->u
.VadFlags
.MemCommit
)
1432 /* This is committed memory */
1435 /* Convert the protection */
1436 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1440 /* Return the protection code */
1441 *ReturnedProtect
= Protect
;
1447 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1448 IN PVOID BaseAddress
,
1449 OUT PVOID MemoryInformation
,
1450 IN SIZE_T MemoryInformationLength
,
1451 OUT PSIZE_T ReturnLength
)
1453 PEPROCESS TargetProcess
;
1454 NTSTATUS Status
= STATUS_SUCCESS
;
1456 PVOID Address
, NextAddress
;
1457 BOOLEAN Found
= FALSE
;
1458 ULONG NewProtect
, NewState
;
1460 MEMORY_BASIC_INFORMATION MemoryInfo
;
1461 KAPC_STATE ApcState
;
1462 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1463 PMEMORY_AREA MemoryArea
;
1464 SIZE_T ResultLength
;
1466 /* Check for illegal addresses in user-space, or the shared memory area */
1467 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1468 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1470 Address
= PAGE_ALIGN(BaseAddress
);
1472 /* Make up an info structure describing this range */
1473 MemoryInfo
.BaseAddress
= Address
;
1474 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1475 MemoryInfo
.Type
= MEM_PRIVATE
;
1477 /* Special case for shared data */
1478 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1480 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1481 MemoryInfo
.State
= MEM_COMMIT
;
1482 MemoryInfo
.Protect
= PAGE_READONLY
;
1483 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1487 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1488 MemoryInfo
.State
= MEM_RESERVE
;
1489 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1490 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1493 /* Return the data, NtQueryInformation already probed it*/
1494 if (PreviousMode
!= KernelMode
)
1498 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1499 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1501 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1503 Status
= _SEH2_GetExceptionCode();
1509 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1510 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1516 /* Check if this is for a local or remote process */
1517 if (ProcessHandle
== NtCurrentProcess())
1519 TargetProcess
= PsGetCurrentProcess();
1523 /* Reference the target process */
1524 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1525 PROCESS_QUERY_INFORMATION
,
1527 ExGetPreviousMode(),
1528 (PVOID
*)&TargetProcess
,
1530 if (!NT_SUCCESS(Status
)) return Status
;
1532 /* Attach to it now */
1533 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1537 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1538 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1540 /* Scan on the right */
1541 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1542 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1545 /* Check if this VAD covers the allocation range */
1546 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1547 (BaseVpn
<= Vad
->EndingVpn
))
1554 /* Check if this VAD is too high */
1555 if (BaseVpn
< Vad
->StartingVpn
)
1557 /* Stop if there is no left child */
1558 if (!Vad
->LeftChild
) break;
1560 /* Search on the left next */
1561 Vad
= Vad
->LeftChild
;
1565 /* Then this VAD is too low, keep searching on the right */
1566 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1568 /* Stop if there is no right child */
1569 if (!Vad
->RightChild
) break;
1571 /* Search on the right next */
1572 Vad
= Vad
->RightChild
;
1577 /* Was a VAD found? */
1580 Address
= PAGE_ALIGN(BaseAddress
);
1582 /* Calculate region size */
1585 if (Vad
->StartingVpn
>= BaseVpn
)
1587 /* Region size is the free space till the start of that VAD */
1588 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1592 /* Get the next VAD */
1593 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1596 /* Region size is the free space till the start of that VAD */
1597 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1601 /* Maximum possible region size with that base address */
1602 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1608 /* Maximum possible region size with that base address */
1609 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1612 /* Check if we were attached */
1613 if (ProcessHandle
!= NtCurrentProcess())
1615 /* Detach and derefernece the process */
1616 KeUnstackDetachProcess(&ApcState
);
1617 ObDereferenceObject(TargetProcess
);
1620 /* Build the rest of the initial information block */
1621 MemoryInfo
.BaseAddress
= Address
;
1622 MemoryInfo
.AllocationBase
= NULL
;
1623 MemoryInfo
.AllocationProtect
= 0;
1624 MemoryInfo
.State
= MEM_FREE
;
1625 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1626 MemoryInfo
.Type
= 0;
1628 /* Return the data, NtQueryInformation already probed it*/
1629 if (PreviousMode
!= KernelMode
)
1633 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1634 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1636 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1638 Status
= _SEH2_GetExceptionCode();
1644 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1645 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1651 /* Set the correct memory type based on what kind of VAD this is */
1652 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1653 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1655 MemoryInfo
.Type
= MEM_PRIVATE
;
1657 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1659 MemoryInfo
.Type
= MEM_IMAGE
;
1663 MemoryInfo
.Type
= MEM_MAPPED
;
1666 /* Lock the address space of the process */
1667 MmLockAddressSpace(&TargetProcess
->Vm
);
1669 /* Find the memory area the specified address belongs to */
1670 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1671 ASSERT(MemoryArea
!= NULL
);
1673 /* Determine information dependent on the memory area type */
1674 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1676 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1677 ASSERT(NT_SUCCESS(Status
));
1681 /* Build the initial information block */
1682 Address
= PAGE_ALIGN(BaseAddress
);
1683 MemoryInfo
.BaseAddress
= Address
;
1684 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1685 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1686 MemoryInfo
.Type
= MEM_PRIVATE
;
1688 /* Find the largest chunk of memory which has the same state and protection mask */
1689 MemoryInfo
.State
= MiQueryAddressState(Address
,
1692 &MemoryInfo
.Protect
,
1694 Address
= NextAddress
;
1695 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1697 /* Keep going unless the state or protection mask changed */
1698 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1699 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1700 Address
= NextAddress
;
1703 /* Now that we know the last VA address, calculate the region size */
1704 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1707 /* Unlock the address space of the process */
1708 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1710 /* Check if we were attached */
1711 if (ProcessHandle
!= NtCurrentProcess())
1713 /* Detach and derefernece the process */
1714 KeUnstackDetachProcess(&ApcState
);
1715 ObDereferenceObject(TargetProcess
);
1718 /* Return the data, NtQueryInformation already probed it*/
1719 if (PreviousMode
!= KernelMode
)
1723 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1724 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1726 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1728 Status
= _SEH2_GetExceptionCode();
1734 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1735 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1739 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1740 "State: %lx Type: %lx Size: %lx\n",
1741 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1742 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1743 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1750 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1751 IN ULONG_PTR EndingAddress
,
1753 IN PEPROCESS Process
)
1755 PMMPTE PointerPte
, LastPte
, PointerPde
;
1756 BOOLEAN OnBoundary
= TRUE
;
1759 /* Get the PDE and PTE addresses */
1760 PointerPde
= MiAddressToPde(StartingAddress
);
1761 PointerPte
= MiAddressToPte(StartingAddress
);
1762 LastPte
= MiAddressToPte(EndingAddress
);
1764 /* Loop all the PTEs */
1765 while (PointerPte
<= LastPte
)
1767 /* Check if we've hit an new PDE boundary */
1770 /* Is this PDE demand zero? */
1771 PointerPde
= MiAddressToPte(PointerPte
);
1772 if (PointerPde
->u
.Long
!= 0)
1774 /* It isn't -- is it valid? */
1775 if (PointerPde
->u
.Hard
.Valid
== 0)
1777 /* Nope, fault it in */
1778 PointerPte
= MiPteToAddress(PointerPde
);
1779 MiMakeSystemAddressValid(PointerPte
, Process
);
1784 /* The PTE was already valid, so move to the next one */
1786 PointerPte
= MiPteToAddress(PointerPde
);
1788 /* Is the entire VAD committed? If not, fail */
1789 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1791 /* Everything is committed so far past the range, return true */
1792 if (PointerPte
> LastPte
) return TRUE
;
1796 /* Is the PTE demand zero? */
1797 if (PointerPte
->u
.Long
== 0)
1799 /* Is the entire VAD committed? If not, fail */
1800 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1804 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1805 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1806 (PointerPte
->u
.Hard
.Valid
== 0) &&
1807 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1808 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1810 /* Then part of the range is decommitted, so fail */
1815 /* Move to the next PTE */
1817 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1820 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1826 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1827 IN OUT PVOID
*BaseAddress
,
1828 IN OUT PSIZE_T NumberOfBytesToProtect
,
1829 IN ULONG NewAccessProtection
,
1830 OUT PULONG OldAccessProtection OPTIONAL
)
1832 PMEMORY_AREA MemoryArea
;
1833 PMMSUPPORT AddressSpace
;
1834 ULONG OldAccessProtection_
;
1837 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1838 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1840 AddressSpace
= &Process
->Vm
;
1841 MmLockAddressSpace(AddressSpace
);
1842 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
1843 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
1845 MmUnlockAddressSpace(AddressSpace
);
1846 return STATUS_UNSUCCESSFUL
;
1849 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
1851 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
1852 Status
= MmProtectSectionView(AddressSpace
,
1855 *NumberOfBytesToProtect
,
1856 NewAccessProtection
,
1857 OldAccessProtection
);
1859 MmUnlockAddressSpace(AddressSpace
);
1866 MiProtectVirtualMemory(IN PEPROCESS Process
,
1867 IN OUT PVOID
*BaseAddress
,
1868 IN OUT PSIZE_T NumberOfBytesToProtect
,
1869 IN ULONG NewAccessProtection
,
1870 OUT PULONG OldAccessProtection OPTIONAL
)
1872 PMEMORY_AREA MemoryArea
;
1874 PMMSUPPORT AddressSpace
;
1875 ULONG_PTR StartingAddress
, EndingAddress
;
1876 PMMPTE PointerPde
, PointerPte
, LastPte
;
1879 ULONG ProtectionMask
, OldProtect
;
1881 NTSTATUS Status
= STATUS_SUCCESS
;
1882 PETHREAD Thread
= PsGetCurrentThread();
1884 /* Calculate base address for the VAD */
1885 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1886 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1888 /* Calculate the protection mask and make sure it's valid */
1889 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1890 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1892 DPRINT1("Invalid protection mask\n");
1893 return STATUS_INVALID_PAGE_PROTECTION
;
1896 /* Check for ROS specific memory area */
1897 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1898 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1901 return MiRosProtectVirtualMemory(Process
,
1903 NumberOfBytesToProtect
,
1904 NewAccessProtection
,
1905 OldAccessProtection
);
1908 /* Lock the address space and make sure the process isn't already dead */
1909 AddressSpace
= MmGetCurrentAddressSpace();
1910 MmLockAddressSpace(AddressSpace
);
1911 if (Process
->VmDeleted
)
1913 DPRINT1("Process is dying\n");
1914 Status
= STATUS_PROCESS_IS_TERMINATING
;
1918 /* Get the VAD for this address range, and make sure it exists */
1919 Vad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
1920 EndingAddress
>> PAGE_SHIFT
,
1924 DPRINT("Could not find a VAD for this allocation\n");
1925 Status
= STATUS_CONFLICTING_ADDRESSES
;
1929 /* Make sure the address is within this VAD's boundaries */
1930 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
1931 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
1933 Status
= STATUS_CONFLICTING_ADDRESSES
;
1937 /* These kinds of VADs are not supported atm */
1938 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
1939 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
1940 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
1942 DPRINT1("Illegal VAD for attempting to set protection\n");
1943 Status
= STATUS_CONFLICTING_ADDRESSES
;
1947 /* Check for a VAD whose protection can't be changed */
1948 if (Vad
->u
.VadFlags
.NoChange
== 1)
1950 DPRINT1("Trying to change protection of a NoChange VAD\n");
1951 Status
= STATUS_INVALID_PAGE_PROTECTION
;
1955 /* Is this section, or private memory? */
1956 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
1958 /* Not yet supported */
1959 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
1961 DPRINT1("Illegal VAD for attempting to set protection\n");
1962 Status
= STATUS_CONFLICTING_ADDRESSES
;
1966 /* Rotate VADs are not yet supported */
1967 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
1969 DPRINT1("Illegal VAD for attempting to set protection\n");
1970 Status
= STATUS_CONFLICTING_ADDRESSES
;
1974 /* Not valid on section files */
1975 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
1978 DPRINT1("Invalid protection flags for section\n");
1979 Status
= STATUS_INVALID_PARAMETER_4
;
1983 /* Check if data or page file mapping protection PTE is compatible */
1984 if (!Vad
->ControlArea
->u
.Flags
.Image
)
1987 DPRINT1("Fixme: Not checking for valid protection\n");
1990 /* This is a section, and this is not yet supported */
1991 DPRINT1("Section protection not yet supported\n");
1996 /* Private memory, check protection flags */
1997 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
1998 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2000 DPRINT1("Invalid protection flags for private memory\n");
2001 Status
= STATUS_INVALID_PARAMETER_4
;
2005 /* Lock the working set */
2006 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2008 /* Check if all pages in this range are committed */
2009 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2016 DPRINT1("The entire range is not committed\n");
2017 Status
= STATUS_NOT_COMMITTED
;
2018 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2022 /* Compute starting and ending PTE and PDE addresses */
2023 PointerPde
= MiAddressToPde(StartingAddress
);
2024 PointerPte
= MiAddressToPte(StartingAddress
);
2025 LastPte
= MiAddressToPte(EndingAddress
);
2027 /* Make this PDE valid */
2028 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2030 /* Save protection of the first page */
2031 if (PointerPte
->u
.Long
!= 0)
2033 /* Capture the page protection and make the PDE valid */
2034 OldProtect
= MiGetPageProtection(PointerPte
);
2035 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2039 /* Grab the old protection from the VAD itself */
2040 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2043 /* Loop all the PTEs now */
2044 while (PointerPte
<= LastPte
)
2046 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2047 if (MiIsPteOnPdeBoundary(PointerPte
))
2049 PointerPde
= MiAddressToPte(PointerPte
);
2050 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2053 /* Capture the PTE and check if it was empty */
2054 PteContents
= *PointerPte
;
2055 if (PteContents
.u
.Long
== 0)
2057 /* This used to be a zero PTE and it no longer is, so we must add a
2058 reference to the pagetable. */
2059 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2062 /* Check what kind of PTE we are dealing with */
2063 if (PteContents
.u
.Hard
.Valid
== 1)
2065 /* Get the PFN entry */
2066 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2068 /* We don't support this yet */
2069 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2071 /* Check if the page should not be accessible at all */
2072 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2073 (NewAccessProtection
& PAGE_GUARD
))
2075 /* The page should be in the WS and we should make it transition now */
2076 DPRINT1("Making valid page invalid is not yet supported!\n");
2077 Status
= STATUS_NOT_IMPLEMENTED
;
2078 /* Unlock the working set */
2079 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2083 /* Write the protection mask and write it with a TLB flush */
2084 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2085 MiFlushTbAndCapture(Vad
,
2093 /* We don't support these cases yet */
2094 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2095 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2097 /* The PTE is already demand-zero, just update the protection mask */
2098 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
2099 ASSERT(PointerPte
->u
.Long
!= 0);
2102 /* Move to the next PTE */
2106 /* Unlock the working set */
2107 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2110 /* Unlock the address space */
2111 MmUnlockAddressSpace(AddressSpace
);
2113 /* Return parameters and success */
2114 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2115 *BaseAddress
= (PVOID
)StartingAddress
;
2116 *OldAccessProtection
= OldProtect
;
2117 return STATUS_SUCCESS
;
2120 /* Unlock the address space and return the failure code */
2121 MmUnlockAddressSpace(AddressSpace
);
2127 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2128 IN PEPROCESS TargetProcess
,
2131 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2134 // Sanity checks. The latter is because we only use this function with the
2135 // PFN lock not held, so it may go away in the future.
2137 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2138 ASSERT(OldIrql
== MM_NOIRQL
);
2141 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2142 // return the same address as the PDE on 2-level page table systems.
2144 // If everything is already valid, there is nothing to do.
2146 PointerPpe
= MiAddressToPte(PointerPde
);
2147 PointerPxe
= MiAddressToPde(PointerPde
);
2148 if ((PointerPxe
->u
.Hard
.Valid
) &&
2149 (PointerPpe
->u
.Hard
.Valid
) &&
2150 (PointerPde
->u
.Hard
.Valid
))
2156 // At least something is invalid, so begin by getting the PTE for the PDE itself
2157 // and then lookup each additional level. We must do it in this precise order
2158 // because the pagfault.c code (as well as in Windows) depends that the next
2159 // level up (higher) must be valid when faulting a lower level
2161 PointerPte
= MiPteToAddress(PointerPde
);
2165 // Make sure APCs continued to be disabled
2167 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2170 // First, make the PXE valid if needed
2172 if (!PointerPxe
->u
.Hard
.Valid
)
2174 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2175 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2181 if (!PointerPpe
->u
.Hard
.Valid
)
2183 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2184 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2188 // And finally, make the PDE itself valid.
2190 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2193 // This should've worked the first time so the loop is really just for
2194 // show -- ASSERT that we're actually NOT going to be looping.
2196 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2197 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2198 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2199 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2200 !(PointerPpe
->u
.Hard
.Valid
) ||
2201 !(PointerPde
->u
.Hard
.Valid
));
2206 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2212 PFN_NUMBER PageFrameIndex
;
2216 // Acquire the PFN lock and loop all the PTEs in the list
2218 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2219 for (i
= 0; i
!= Count
; i
++)
2222 // The PTE must currently be valid
2224 TempPte
= *ValidPteList
[i
];
2225 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2228 // Get the PFN entry for the page itself, and then for its page table
2230 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2231 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2232 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2235 // Decrement the share count on the page table, and then on the page
2238 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2239 MI_SET_PFN_DELETED(Pfn1
);
2240 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2243 // Make the page decommitted
2245 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2249 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2250 // and then release the PFN lock
2253 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2258 MiDecommitPages(IN PVOID StartingAddress
,
2259 IN PMMPTE EndingPte
,
2260 IN PEPROCESS Process
,
2263 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2264 ULONG CommitReduction
= 0;
2265 PMMPTE ValidPteList
[256];
2269 PETHREAD CurrentThread
= PsGetCurrentThread();
2272 // Get the PTE and PTE for the address, and lock the working set
2273 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2274 // commited range ends so that we can do the right accounting.
2276 PointerPde
= MiAddressToPde(StartingAddress
);
2277 PointerPte
= MiAddressToPte(StartingAddress
);
2278 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2279 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2282 // Make the PDE valid, and now loop through each page's worth of data
2284 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2285 while (PointerPte
<= EndingPte
)
2288 // Check if we've crossed a PDE boundary
2290 if (MiIsPteOnPdeBoundary(PointerPte
))
2293 // Get the new PDE and flush the valid PTEs we had built up until
2294 // now. This helps reduce the amount of TLB flushing we have to do.
2295 // Note that Windows does a much better job using timestamps and
2296 // such, and does not flush the entire TLB all the time, but right
2297 // now we have bigger problems to worry about than TLB flushing.
2299 PointerPde
= MiAddressToPde(StartingAddress
);
2302 MiProcessValidPteList(ValidPteList
, PteCount
);
2307 // Make this PDE valid
2309 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2313 // Read this PTE. It might be active or still demand-zero.
2315 PteContents
= *PointerPte
;
2316 if (PteContents
.u
.Long
)
2319 // The PTE is active. It might be valid and in a working set, or
2320 // it might be a prototype PTE or paged out or even in transition.
2322 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2325 // It's already decommited, so there's nothing for us to do here
2332 // Remove it from the counters, and check if it was valid or not
2334 //Process->NumberOfPrivatePages--;
2335 if (PteContents
.u
.Hard
.Valid
)
2338 // It's valid. At this point make sure that it is not a ROS
2339 // PFN. Also, we don't support ProtoPTEs in this code path.
2341 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2342 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2343 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2346 // Flush any pending PTEs that we had not yet flushed, if our
2347 // list has gotten too big, then add this PTE to the flush list.
2349 if (PteCount
== 256)
2351 MiProcessValidPteList(ValidPteList
, PteCount
);
2354 ValidPteList
[PteCount
++] = PointerPte
;
2359 // We do not support any of these other scenarios at the moment
2361 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2362 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2363 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2366 // So the only other possibility is that it is still a demand
2367 // zero PTE, in which case we undo the accounting we did
2368 // earlier and simply make the page decommitted.
2370 //Process->NumberOfPrivatePages++;
2371 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2378 // This used to be a zero PTE and it no longer is, so we must add a
2379 // reference to the pagetable.
2381 MiIncrementPageTableReferences(StartingAddress
);
2384 // Next, we account for decommitted PTEs and make the PTE as such
2386 if (PointerPte
> CommitPte
) CommitReduction
++;
2387 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2391 // Move to the next PTE and the next address
2394 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2398 // Flush any dangling PTEs from the loop in the last page table, and then
2399 // release the working set and return the commit reduction accounting.
2401 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2402 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2403 return CommitReduction
;
2406 /* PUBLIC FUNCTIONS ***********************************************************/
2413 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2424 MmSecureVirtualMemory(IN PVOID Address
,
2428 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2437 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2439 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2442 /* SYSTEM CALLS ***************************************************************/
2446 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2447 IN PVOID BaseAddress
,
2449 IN SIZE_T NumberOfBytesToRead
,
2450 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2452 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2454 NTSTATUS Status
= STATUS_SUCCESS
;
2455 SIZE_T BytesRead
= 0;
2459 // Check if we came from user mode
2461 if (PreviousMode
!= KernelMode
)
2464 // Validate the read addresses
2466 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2467 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2468 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2469 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2472 // Don't allow to write into kernel space
2474 return STATUS_ACCESS_VIOLATION
;
2478 // Enter SEH for probe
2483 // Probe the output value
2485 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2490 // Get exception code
2492 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2498 // Don't do zero-byte transfers
2500 if (NumberOfBytesToRead
)
2503 // Reference the process
2505 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2511 if (NT_SUCCESS(Status
))
2516 Status
= MmCopyVirtualMemory(Process
,
2518 PsGetCurrentProcess(),
2520 NumberOfBytesToRead
,
2525 // Dereference the process
2527 ObDereferenceObject(Process
);
2532 // Check if the caller sent this parameter
2534 if (NumberOfBytesRead
)
2537 // Enter SEH to guard write
2542 // Return the number of bytes read
2544 *NumberOfBytesRead
= BytesRead
;
2546 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2560 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2561 IN PVOID BaseAddress
,
2563 IN SIZE_T NumberOfBytesToWrite
,
2564 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2566 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2568 NTSTATUS Status
= STATUS_SUCCESS
;
2569 SIZE_T BytesWritten
= 0;
2573 // Check if we came from user mode
2575 if (PreviousMode
!= KernelMode
)
2578 // Validate the read addresses
2580 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2581 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2582 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2583 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2586 // Don't allow to write into kernel space
2588 return STATUS_ACCESS_VIOLATION
;
2592 // Enter SEH for probe
2597 // Probe the output value
2599 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2601 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2604 // Get exception code
2606 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2612 // Don't do zero-byte transfers
2614 if (NumberOfBytesToWrite
)
2617 // Reference the process
2619 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2625 if (NT_SUCCESS(Status
))
2630 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2634 NumberOfBytesToWrite
,
2639 // Dereference the process
2641 ObDereferenceObject(Process
);
2646 // Check if the caller sent this parameter
2648 if (NumberOfBytesWritten
)
2651 // Enter SEH to guard write
2656 // Return the number of bytes written
2658 *NumberOfBytesWritten
= BytesWritten
;
2660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2674 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2675 IN OUT PVOID
*UnsafeBaseAddress
,
2676 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2677 IN ULONG NewAccessProtection
,
2678 OUT PULONG UnsafeOldAccessProtection
)
2681 ULONG OldAccessProtection
;
2683 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2684 PVOID BaseAddress
= NULL
;
2685 SIZE_T NumberOfBytesToProtect
= 0;
2686 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2688 BOOLEAN Attached
= FALSE
;
2689 KAPC_STATE ApcState
;
2693 // Check for valid protection flags
2695 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2696 if (Protection
!= PAGE_NOACCESS
&&
2697 Protection
!= PAGE_READONLY
&&
2698 Protection
!= PAGE_READWRITE
&&
2699 Protection
!= PAGE_WRITECOPY
&&
2700 Protection
!= PAGE_EXECUTE
&&
2701 Protection
!= PAGE_EXECUTE_READ
&&
2702 Protection
!= PAGE_EXECUTE_READWRITE
&&
2703 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2708 return STATUS_INVALID_PAGE_PROTECTION
;
2712 // Check if we came from user mode
2714 if (PreviousMode
!= KernelMode
)
2717 // Enter SEH for probing
2722 // Validate all outputs
2724 ProbeForWritePointer(UnsafeBaseAddress
);
2725 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2726 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2731 BaseAddress
= *UnsafeBaseAddress
;
2732 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2734 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2737 // Get exception code
2739 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2748 BaseAddress
= *UnsafeBaseAddress
;
2749 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2753 // Catch illegal base address
2755 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2758 // Catch illegal region size
2760 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2765 return STATUS_INVALID_PARAMETER_3
;
2769 // 0 is also illegal
2771 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2774 // Get a reference to the process
2776 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2777 PROCESS_VM_OPERATION
,
2782 if (!NT_SUCCESS(Status
)) return Status
;
2785 // Check if we should attach
2787 if (CurrentProcess
!= Process
)
2792 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2797 // Do the actual work
2799 Status
= MiProtectVirtualMemory(Process
,
2801 &NumberOfBytesToProtect
,
2802 NewAccessProtection
,
2803 &OldAccessProtection
);
2808 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2811 // Release reference
2813 ObDereferenceObject(Process
);
2816 // Enter SEH to return data
2821 // Return data to user
2823 *UnsafeOldAccessProtection
= OldAccessProtection
;
2824 *UnsafeBaseAddress
= BaseAddress
;
2825 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2840 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2841 IN OUT PVOID
*BaseAddress
,
2842 IN OUT PSIZE_T NumberOfBytesToLock
,
2846 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2848 BOOLEAN Attached
= FALSE
;
2849 KAPC_STATE ApcState
;
2850 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2851 PVOID CapturedBaseAddress
;
2852 SIZE_T CapturedBytesToLock
;
2858 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2861 // Invalid set of flags
2863 return STATUS_INVALID_PARAMETER
;
2867 // At least one flag must be specified
2869 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2874 return STATUS_INVALID_PARAMETER
;
2878 // Enter SEH for probing
2883 // Validate output data
2885 ProbeForWritePointer(BaseAddress
);
2886 ProbeForWriteSize_t(NumberOfBytesToLock
);
2891 CapturedBaseAddress
= *BaseAddress
;
2892 CapturedBytesToLock
= *NumberOfBytesToLock
;
2894 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2897 // Get exception code
2899 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2904 // Catch illegal base address
2906 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2909 // Catch illegal region size
2911 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2916 return STATUS_INVALID_PARAMETER
;
2920 // 0 is also illegal
2922 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2925 // Get a reference to the process
2927 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2928 PROCESS_VM_OPERATION
,
2933 if (!NT_SUCCESS(Status
)) return Status
;
2936 // Check if this is is system-mapped
2938 if (MapType
& MAP_SYSTEM
)
2941 // Check for required privilege
2943 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2946 // Fail: Don't have it
2948 ObDereferenceObject(Process
);
2949 return STATUS_PRIVILEGE_NOT_HELD
;
2954 // Check if we should attach
2956 if (CurrentProcess
!= Process
)
2961 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2973 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2976 // Release reference
2978 ObDereferenceObject(Process
);
2981 // Enter SEH to return data
2986 // Return data to user
2988 *BaseAddress
= CapturedBaseAddress
;
2989 *NumberOfBytesToLock
= 0;
2991 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2994 // Get exception code
2996 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3003 return STATUS_SUCCESS
;
3008 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3009 IN OUT PVOID
*BaseAddress
,
3010 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3014 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3016 BOOLEAN Attached
= FALSE
;
3017 KAPC_STATE ApcState
;
3018 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3019 PVOID CapturedBaseAddress
;
3020 SIZE_T CapturedBytesToUnlock
;
3026 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3029 // Invalid set of flags
3031 return STATUS_INVALID_PARAMETER
;
3035 // At least one flag must be specified
3037 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3042 return STATUS_INVALID_PARAMETER
;
3046 // Enter SEH for probing
3051 // Validate output data
3053 ProbeForWritePointer(BaseAddress
);
3054 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3059 CapturedBaseAddress
= *BaseAddress
;
3060 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3062 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3065 // Get exception code
3067 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3072 // Catch illegal base address
3074 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3077 // Catch illegal region size
3079 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3084 return STATUS_INVALID_PARAMETER
;
3088 // 0 is also illegal
3090 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3093 // Get a reference to the process
3095 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3096 PROCESS_VM_OPERATION
,
3101 if (!NT_SUCCESS(Status
)) return Status
;
3104 // Check if this is is system-mapped
3106 if (MapType
& MAP_SYSTEM
)
3109 // Check for required privilege
3111 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3114 // Fail: Don't have it
3116 ObDereferenceObject(Process
);
3117 return STATUS_PRIVILEGE_NOT_HELD
;
3122 // Check if we should attach
3124 if (CurrentProcess
!= Process
)
3129 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3141 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3144 // Release reference
3146 ObDereferenceObject(Process
);
3149 // Enter SEH to return data
3154 // Return data to user
3156 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3157 *NumberOfBytesToUnlock
= 0;
3159 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3162 // Get exception code
3164 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3171 return STATUS_SUCCESS
;
3176 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3177 IN OUT PVOID
*BaseAddress
,
3178 IN OUT PSIZE_T NumberOfBytesToFlush
,
3179 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3183 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3184 PVOID CapturedBaseAddress
;
3185 SIZE_T CapturedBytesToFlush
;
3186 IO_STATUS_BLOCK LocalStatusBlock
;
3190 // Check if we came from user mode
3192 if (PreviousMode
!= KernelMode
)
3195 // Enter SEH for probing
3200 // Validate all outputs
3202 ProbeForWritePointer(BaseAddress
);
3203 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3204 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3209 CapturedBaseAddress
= *BaseAddress
;
3210 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3215 // Get exception code
3217 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3226 CapturedBaseAddress
= *BaseAddress
;
3227 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3231 // Catch illegal base address
3233 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3236 // Catch illegal region size
3238 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3243 return STATUS_INVALID_PARAMETER
;
3247 // Get a reference to the process
3249 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3250 PROCESS_VM_OPERATION
,
3255 if (!NT_SUCCESS(Status
)) return Status
;
3260 Status
= MmFlushVirtualMemory(Process
,
3261 &CapturedBaseAddress
,
3262 &CapturedBytesToFlush
,
3266 // Release reference
3268 ObDereferenceObject(Process
);
3271 // Enter SEH to return data
3276 // Return data to user
3278 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3279 *NumberOfBytesToFlush
= 0;
3280 *IoStatusBlock
= LocalStatusBlock
;
3282 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3298 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3300 IN PVOID BaseAddress
,
3301 IN SIZE_T RegionSize
,
3302 IN PVOID
*UserAddressArray
,
3303 OUT PULONG_PTR EntriesInUserAddressArray
,
3304 OUT PULONG Granularity
)
3309 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3310 ULONG_PTR CapturedEntryCount
;
3314 // Check if we came from user mode
3316 if (PreviousMode
!= KernelMode
)
3319 // Enter SEH for probing
3324 // Catch illegal base address
3326 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3329 // Catch illegal region size
3331 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3336 return STATUS_INVALID_PARAMETER_3
;
3340 // Validate all data
3342 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3343 ProbeForWriteUlong(Granularity
);
3348 CapturedEntryCount
= *EntriesInUserAddressArray
;
3351 // Must have a count
3353 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
3356 // Can't be larger than the maximum
3358 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3363 return STATUS_INVALID_PARAMETER_5
;
3367 // Probe the actual array
3369 ProbeForWrite(UserAddressArray
,
3370 CapturedEntryCount
* sizeof(PVOID
),
3373 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3376 // Get exception code
3378 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3387 CapturedEntryCount
= *EntriesInUserAddressArray
;
3388 ASSERT(CapturedEntryCount
!= 0);
3392 // Check if this is a local request
3394 if (ProcessHandle
== NtCurrentProcess())
3397 // No need to reference the process
3399 Process
= PsGetCurrentProcess();
3404 // Reference the target
3406 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3407 PROCESS_VM_OPERATION
,
3412 if (!NT_SUCCESS(Status
)) return Status
;
3416 // Compute the last address and validate it
3418 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3419 if (BaseAddress
> EndAddress
)
3424 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3425 return STATUS_INVALID_PARAMETER_4
;
3434 // Dereference if needed
3436 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3439 // Enter SEH to return data
3444 // Return data to user
3446 *EntriesInUserAddressArray
= 0;
3447 *Granularity
= PAGE_SIZE
;
3449 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3452 // Get exception code
3454 Status
= _SEH2_GetExceptionCode();
3461 return STATUS_SUCCESS
;
3469 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3470 IN PVOID BaseAddress
,
3471 IN SIZE_T RegionSize
)
3476 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3477 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3480 // Catch illegal base address
3482 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3485 // Catch illegal region size
3487 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3492 return STATUS_INVALID_PARAMETER_3
;
3496 // Check if this is a local request
3498 if (ProcessHandle
== NtCurrentProcess())
3501 // No need to reference the process
3503 Process
= PsGetCurrentProcess();
3508 // Reference the target
3510 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3511 PROCESS_VM_OPERATION
,
3516 if (!NT_SUCCESS(Status
)) return Status
;
3520 // Compute the last address and validate it
3522 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3523 if (BaseAddress
> EndAddress
)
3528 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3529 return STATUS_INVALID_PARAMETER_3
;
3538 // Dereference if needed
3540 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3545 return STATUS_SUCCESS
;
3550 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3551 IN PVOID BaseAddress
,
3552 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3553 OUT PVOID MemoryInformation
,
3554 IN SIZE_T MemoryInformationLength
,
3555 OUT PSIZE_T ReturnLength
)
3557 NTSTATUS Status
= STATUS_SUCCESS
;
3558 KPROCESSOR_MODE PreviousMode
;
3560 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3562 /* Bail out if the address is invalid */
3563 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3565 /* Probe return buffer */
3566 PreviousMode
= ExGetPreviousMode();
3567 if (PreviousMode
!= KernelMode
)
3571 ProbeForWrite(MemoryInformation
,
3572 MemoryInformationLength
,
3575 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3577 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3579 Status
= _SEH2_GetExceptionCode();
3583 if (!NT_SUCCESS(Status
))
3589 switch(MemoryInformationClass
)
3591 case MemoryBasicInformation
:
3592 /* Validate the size information of the class */
3593 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3595 /* The size is invalid */
3596 return STATUS_INFO_LENGTH_MISMATCH
;
3598 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3601 MemoryInformationLength
,
3605 case MemorySectionName
:
3606 /* Validate the size information of the class */
3607 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3609 /* The size is invalid */
3610 return STATUS_INFO_LENGTH_MISMATCH
;
3612 Status
= MiQueryMemorySectionName(ProcessHandle
,
3615 MemoryInformationLength
,
3618 case MemoryWorkingSetList
:
3619 case MemoryBasicVlmInformation
:
3621 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3633 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3634 IN OUT PVOID
* UBaseAddress
,
3635 IN ULONG_PTR ZeroBits
,
3636 IN OUT PSIZE_T URegionSize
,
3637 IN ULONG AllocationType
,
3641 PMEMORY_AREA MemoryArea
;
3642 PFN_NUMBER PageCount
;
3643 PMMVAD Vad
, FoundVad
;
3645 PMMSUPPORT AddressSpace
;
3647 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3648 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3649 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3650 PETHREAD CurrentThread
= PsGetCurrentThread();
3651 KAPC_STATE ApcState
;
3652 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3653 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3655 PMMPTE PointerPte
, PointerPde
, LastPte
;
3658 /* Check for valid Zero bits */
3661 DPRINT1("Too many zero bits\n");
3662 return STATUS_INVALID_PARAMETER_3
;
3665 /* Check for valid Allocation Types */
3666 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3667 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3669 DPRINT1("Invalid Allocation Type\n");
3670 return STATUS_INVALID_PARAMETER_5
;
3673 /* Check for at least one of these Allocation Types to be set */
3674 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3676 DPRINT1("No memory allocation base type\n");
3677 return STATUS_INVALID_PARAMETER_5
;
3680 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3681 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3683 DPRINT1("Invalid use of MEM_RESET\n");
3684 return STATUS_INVALID_PARAMETER_5
;
3687 /* Check if large pages are being used */
3688 if (AllocationType
& MEM_LARGE_PAGES
)
3690 /* Large page allocations MUST be committed */
3691 if (!(AllocationType
& MEM_COMMIT
))
3693 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3694 return STATUS_INVALID_PARAMETER_5
;
3697 /* These flags are not allowed with large page allocations */
3698 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3700 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3701 return STATUS_INVALID_PARAMETER_5
;
3705 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3706 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3708 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3709 return STATUS_INVALID_PARAMETER_5
;
3712 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3713 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3715 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3716 return STATUS_INVALID_PARAMETER_5
;
3719 /* Check for valid MEM_PHYSICAL usage */
3720 if (AllocationType
& MEM_PHYSICAL
)
3722 /* Only these flags are allowed with MEM_PHYSIAL */
3723 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3725 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3726 return STATUS_INVALID_PARAMETER_5
;
3729 /* Then make sure PAGE_READWRITE is used */
3730 if (Protect
!= PAGE_READWRITE
)
3732 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3733 return STATUS_INVALID_PARAMETER_6
;
3737 /* Calculate the protection mask and make sure it's valid */
3738 ProtectionMask
= MiMakeProtectionMask(Protect
);
3739 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3741 DPRINT1("Invalid protection mask\n");
3742 return STATUS_INVALID_PAGE_PROTECTION
;
3748 /* Check for user-mode parameters */
3749 if (PreviousMode
!= KernelMode
)
3751 /* Make sure they are writable */
3752 ProbeForWritePointer(UBaseAddress
);
3753 ProbeForWriteUlong(URegionSize
);
3756 /* Capture their values */
3757 PBaseAddress
= *UBaseAddress
;
3758 PRegionSize
= *URegionSize
;
3760 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3762 /* Return the exception code */
3763 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3767 /* Make sure the allocation isn't past the VAD area */
3768 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3770 DPRINT1("Virtual allocation base above User Space\n");
3771 return STATUS_INVALID_PARAMETER_2
;
3774 /* Make sure the allocation wouldn't overflow past the VAD area */
3775 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3777 DPRINT1("Region size would overflow into kernel-memory\n");
3778 return STATUS_INVALID_PARAMETER_4
;
3781 /* Make sure there's a size specified */
3784 DPRINT1("Region size is invalid (zero)\n");
3785 return STATUS_INVALID_PARAMETER_4
;
3789 // If this is for the current process, just use PsGetCurrentProcess
3791 if (ProcessHandle
== NtCurrentProcess())
3793 Process
= CurrentProcess
;
3798 // Otherwise, reference the process with VM rights and attach to it if
3799 // this isn't the current process. We must attach because we'll be touching
3800 // PTEs and PDEs that belong to user-mode memory, and also touching the
3801 // Working Set which is stored in Hyperspace.
3803 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3804 PROCESS_VM_OPERATION
,
3809 if (!NT_SUCCESS(Status
)) return Status
;
3810 if (CurrentProcess
!= Process
)
3812 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3818 // Check for large page allocations and make sure that the required privilege
3819 // is being held, before attempting to handle them.
3821 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3822 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3824 /* Fail without it */
3825 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3826 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3827 goto FailPathNoLock
;
3831 // Fail on the things we don't yet support
3835 DPRINT1("Zero bits not supported\n");
3836 Status
= STATUS_INVALID_PARAMETER
;
3837 goto FailPathNoLock
;
3839 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
3841 DPRINT1("MEM_LARGE_PAGES not supported\n");
3842 Status
= STATUS_INVALID_PARAMETER
;
3843 goto FailPathNoLock
;
3845 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
3847 DPRINT1("MEM_PHYSICAL not supported\n");
3848 Status
= STATUS_INVALID_PARAMETER
;
3849 goto FailPathNoLock
;
3851 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
3853 DPRINT1("MEM_WRITE_WATCH not supported\n");
3854 Status
= STATUS_INVALID_PARAMETER
;
3855 goto FailPathNoLock
;
3857 if ((AllocationType
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
)
3859 DPRINT1("MEM_TOP_DOWN not supported\n");
3860 AllocationType
&= ~MEM_TOP_DOWN
;
3863 if (Process
->VmTopDown
== 1)
3865 DPRINT1("VmTopDown not supported\n");
3866 Status
= STATUS_INVALID_PARAMETER
;
3867 goto FailPathNoLock
;
3871 // Check if the caller is reserving memory, or committing memory and letting
3872 // us pick the base address
3874 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3877 // Do not allow COPY_ON_WRITE through this API
3879 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3881 DPRINT1("Copy on write not allowed through this path\n");
3882 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3883 goto FailPathNoLock
;
3887 // Does the caller have an address in mind, or is this a blind commit?
3892 // This is a blind commit, all we need is the region size
3894 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3895 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3897 StartingAddress
= 0;
3902 // This is a reservation, so compute the starting address on the
3903 // expected 64KB granularity, and see where the ending address will
3904 // fall based on the aligned address and the passed in region size
3906 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3907 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3908 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3912 // Allocate and initialize the VAD
3914 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3915 ASSERT(Vad
!= NULL
);
3916 Vad
->u
.LongFlags
= 0;
3917 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3918 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3919 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3920 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3923 // Lock the address space and make sure the process isn't already dead
3925 AddressSpace
= MmGetCurrentAddressSpace();
3926 MmLockAddressSpace(AddressSpace
);
3927 if (Process
->VmDeleted
)
3929 Status
= STATUS_PROCESS_IS_TERMINATING
;
3934 // Did we have a base address? If no, find a valid address that is 64KB
3935 // aligned in the VAD tree. Otherwise, make sure that the address range
3936 // which was passed in isn't already conflicting with an existing address
3941 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3944 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3946 if (!NT_SUCCESS(Status
)) goto FailPath
;
3949 // Now we know where the allocation ends. Make sure it doesn't end up
3950 // somewhere in kernel mode.
3952 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3953 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3955 Status
= STATUS_NO_MEMORY
;
3959 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3960 EndingAddress
>> PAGE_SHIFT
,
3964 // The address specified is in conflict!
3966 Status
= STATUS_CONFLICTING_ADDRESSES
;
3971 // Write out the VAD fields for this allocation
3973 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3974 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3977 // FIXME: Should setup VAD bitmap
3979 Status
= STATUS_SUCCESS
;
3982 // Lock the working set and insert the VAD into the process VAD tree
3984 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
3985 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3986 MiInsertVad(Vad
, Process
);
3987 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
3990 // Update the virtual size of the process, and if this is now the highest
3991 // virtual size we have ever seen, update the peak virtual size to reflect
3994 Process
->VirtualSize
+= PRegionSize
;
3995 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
3997 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4001 // Release address space and detach and dereference the target process if
4002 // it was different from the current process
4004 MmUnlockAddressSpace(AddressSpace
);
4005 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4006 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4009 // Use SEH to write back the base address and the region size. In the case
4010 // of an exception, we do not return back the exception code, as the memory
4011 // *has* been allocated. The caller would now have to call VirtualQuery
4012 // or do some other similar trick to actually find out where its memory
4013 // allocation ended up
4017 *URegionSize
= PRegionSize
;
4018 *UBaseAddress
= (PVOID
)StartingAddress
;
4020 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4024 return STATUS_SUCCESS
;
4028 // This is a MEM_COMMIT on top of an existing address which must have been
4029 // MEM_RESERVED already. Compute the start and ending base addresses based
4030 // on the user input, and then compute the actual region size once all the
4031 // alignments have been done.
4033 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4034 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4035 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4038 // Lock the address space and make sure the process isn't already dead
4040 AddressSpace
= MmGetCurrentAddressSpace();
4041 MmLockAddressSpace(AddressSpace
);
4042 if (Process
->VmDeleted
)
4044 DPRINT1("Process is dying\n");
4045 Status
= STATUS_PROCESS_IS_TERMINATING
;
4050 // Get the VAD for this address range, and make sure it exists
4052 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4053 EndingAddress
>> PAGE_SHIFT
,
4057 DPRINT1("Could not find a VAD for this allocation\n");
4058 Status
= STATUS_CONFLICTING_ADDRESSES
;
4062 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4064 /// @todo HACK: pretend success
4065 DPRINT("MEM_RESET not supported\n");
4066 Status
= STATUS_SUCCESS
;
4071 // These kinds of VADs are illegal for this Windows function when trying to
4072 // commit an existing range
4074 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4075 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4076 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4078 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4079 Status
= STATUS_CONFLICTING_ADDRESSES
;
4084 // Make sure that this address range actually fits within the VAD for it
4086 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4087 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4089 DPRINT1("Address range does not fit into the VAD\n");
4090 Status
= STATUS_CONFLICTING_ADDRESSES
;
4095 // Make sure this is an ARM3 section
4097 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4098 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4100 // Is this a previously reserved section being committed? If so, enter the
4101 // special section path
4103 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4106 // You cannot commit large page sections through this API
4108 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4110 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4111 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4116 // You can only use caching flags on a rotate VAD
4118 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4119 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4121 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4122 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4127 // We should make sure that the section's permissions aren't being
4130 if (FoundVad
->u
.VadFlags
.NoChange
)
4133 // Make sure it's okay to touch it
4135 Status
= MiCheckSecuredVad(FoundVad
,
4139 if (!NT_SUCCESS(Status
))
4141 DPRINT1("Secured VAD being messed around with\n");
4147 // ARM3 does not support file-backed sections, only shared memory
4149 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4152 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4154 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4155 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4157 DPRINT1("Invalid page protection for rotate VAD\n");
4158 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4163 // Compute PTE addresses and the quota charge, then grab the commit lock
4165 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4166 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4167 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4168 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4171 // Get the segment template PTE and start looping each page
4173 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4174 ASSERT(TempPte
.u
.Long
!= 0);
4175 while (PointerPte
<= LastPte
)
4178 // For each non-already-committed page, write the invalid template PTE
4180 if (PointerPte
->u
.Long
== 0)
4182 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4192 // Now do the commit accounting and release the lock
4194 ASSERT(QuotaCharge
>= QuotaFree
);
4195 QuotaCharge
-= QuotaFree
;
4196 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4197 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4200 // We are done with committing the section pages
4202 Status
= STATUS_SUCCESS
;
4207 // This is a specific ReactOS check because we only use normal VADs
4209 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4212 // While this is an actual Windows check
4214 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4217 // Throw out attempts to use copy-on-write through this API path
4219 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4221 DPRINT1("Write copy attempted when not allowed\n");
4222 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4227 // Initialize a demand-zero PTE
4230 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4233 // Get the PTE, PDE and the last PTE for this address range
4235 PointerPde
= MiAddressToPde(StartingAddress
);
4236 PointerPte
= MiAddressToPte(StartingAddress
);
4237 LastPte
= MiAddressToPte(EndingAddress
);
4240 // Update the commit charge in the VAD as well as in the process, and check
4241 // if this commit charge was now higher than the last recorded peak, in which
4242 // case we also update the peak
4244 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4245 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4246 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4248 Process
->CommitChargePeak
= Process
->CommitCharge
;
4252 // Lock the working set while we play with user pages and page tables
4254 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4257 // Make the current page table valid, and then loop each page within it
4259 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4260 while (PointerPte
<= LastPte
)
4263 // Have we crossed into a new page table?
4265 if (MiIsPteOnPdeBoundary(PointerPte
))
4268 // Get the PDE and now make it valid too
4270 PointerPde
= MiAddressToPte(PointerPte
);
4271 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4275 // Is this a zero PTE as expected?
4277 if (PointerPte
->u
.Long
== 0)
4280 // First increment the count of pages in the page table for this
4283 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4286 // And now write the invalid demand-zero PTE as requested
4288 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4290 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4293 // If the PTE was already decommitted, there is nothing else to do
4294 // but to write the new demand-zero PTE
4296 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4298 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4301 // We don't handle these scenarios yet
4303 if (PointerPte
->u
.Soft
.Valid
== 0)
4305 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4306 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4310 // There's a change in protection, remember this for later, but do
4311 // not yet handle it.
4313 ChangeProtection
= TRUE
;
4317 // Move to the next PTE
4323 // Release the working set lock, unlock the address space, and detach from
4324 // the target process if it was not the current process. Also dereference the
4325 // target process if this wasn't the case.
4327 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4328 Status
= STATUS_SUCCESS
;
4330 MmUnlockAddressSpace(AddressSpace
);
4333 // Check if we need to update the protection
4335 if (ChangeProtection
)
4337 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
4338 SIZE_T ProtectSize
= PRegionSize
;
4339 ULONG OldProtection
;
4342 // Change the protection of the region
4344 MiProtectVirtualMemory(Process
,
4345 &ProtectBaseAddress
,
4352 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4353 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4356 // Use SEH to write back the base address and the region size. In the case
4357 // of an exception, we strangely do return back the exception code, even
4358 // though the memory *has* been allocated. This mimics Windows behavior and
4359 // there is not much we can do about it.
4363 *URegionSize
= PRegionSize
;
4364 *UBaseAddress
= (PVOID
)StartingAddress
;
4366 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4368 Status
= _SEH2_GetExceptionCode();
4379 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4380 IN PVOID
* UBaseAddress
,
4381 IN PSIZE_T URegionSize
,
4384 PMEMORY_AREA MemoryArea
;
4387 ULONG_PTR CommitReduction
= 0;
4388 ULONG_PTR StartingAddress
, EndingAddress
;
4392 PMMSUPPORT AddressSpace
;
4393 PETHREAD CurrentThread
= PsGetCurrentThread();
4394 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4395 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4396 KAPC_STATE ApcState
;
4397 BOOLEAN Attached
= FALSE
;
4401 // Only two flags are supported
4403 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4405 DPRINT1("Invalid FreeType\n");
4406 return STATUS_INVALID_PARAMETER_4
;
4410 // Check if no flag was used, or if both flags were used
4412 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
4413 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
4415 DPRINT1("Invalid FreeType combination\n");
4416 return STATUS_INVALID_PARAMETER_4
;
4420 // Enter SEH for probe and capture. On failure, return back to the caller
4421 // with an exception violation.
4426 // Check for user-mode parameters and make sure that they are writeable
4428 if (PreviousMode
!= KernelMode
)
4430 ProbeForWritePointer(UBaseAddress
);
4431 ProbeForWriteUlong(URegionSize
);
4435 // Capture the current values
4437 PBaseAddress
= *UBaseAddress
;
4438 PRegionSize
= *URegionSize
;
4440 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4442 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4447 // Make sure the allocation isn't past the user area
4449 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4451 DPRINT1("Virtual free base above User Space\n");
4452 return STATUS_INVALID_PARAMETER_2
;
4456 // Make sure the allocation wouldn't overflow past the user area
4458 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4460 DPRINT1("Region size would overflow into kernel-memory\n");
4461 return STATUS_INVALID_PARAMETER_3
;
4465 // If this is for the current process, just use PsGetCurrentProcess
4467 if (ProcessHandle
== NtCurrentProcess())
4469 Process
= CurrentProcess
;
4474 // Otherwise, reference the process with VM rights and attach to it if
4475 // this isn't the current process. We must attach because we'll be touching
4476 // PTEs and PDEs that belong to user-mode memory, and also touching the
4477 // Working Set which is stored in Hyperspace.
4479 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4480 PROCESS_VM_OPERATION
,
4485 if (!NT_SUCCESS(Status
)) return Status
;
4486 if (CurrentProcess
!= Process
)
4488 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4494 // Lock the address space
4496 AddressSpace
= MmGetCurrentAddressSpace();
4497 MmLockAddressSpace(AddressSpace
);
4500 // If the address space is being deleted, fail the de-allocation since it's
4501 // too late to do anything about it
4503 if (Process
->VmDeleted
)
4505 DPRINT1("Process is dead\n");
4506 Status
= STATUS_PROCESS_IS_TERMINATING
;
4511 // Compute start and end addresses, and locate the VAD
4513 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4514 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4515 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4518 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4519 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4524 // If the range exceeds the VAD's ending VPN, fail this request
4526 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4528 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4529 Status
= STATUS_UNABLE_TO_FREE_VM
;
4534 // Only private memory (except rotate VADs) can be freed through here */
4536 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
4537 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
4538 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
4540 DPRINT1("Attempt to free section memory\n");
4541 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
4546 // ARM3 does not yet handle protected VM
4548 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4551 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4552 // and that is is an ARM3 memory area, and not a section view, as we currently
4553 // don't support freeing those though this interface.
4555 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4557 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4560 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4562 if (FreeType
& MEM_RELEASE
)
4565 // ARM3 only supports this VAD in this path
4567 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4570 // Is the caller trying to remove the whole VAD, or remove only a portion
4571 // of it? If no region size is specified, then the assumption is that the
4572 // whole VAD is to be destroyed
4577 // The caller must specify the base address identically to the range
4578 // that is stored in the VAD.
4580 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4582 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4583 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4588 // Now compute the actual start/end addresses based on the VAD
4590 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4591 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4594 // Finally lock the working set and remove the VAD from the VAD tree
4596 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4597 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4598 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4603 // This means the caller wants to release a specific region within
4604 // the range. We have to find out which range this is -- the following
4605 // possibilities exist plus their union (CASE D):
4607 // STARTING ADDRESS ENDING ADDRESS
4608 // [<========][========================================][=========>]
4609 // CASE A CASE B CASE C
4612 // First, check for case A or D
4614 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4619 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4622 // This is the easiest one to handle -- it is identical to
4623 // the code path above when the caller sets a zero region size
4624 // and the whole VAD is destroyed
4626 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4627 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4628 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4633 // This case is pretty easy too -- we compute a bunch of
4634 // pages to decommit, and then push the VAD's starting address
4635 // a bit further down, then decrement the commit charge
4637 // NOT YET IMPLEMENTED IN ARM3.
4639 DPRINT1("Case A not handled\n");
4640 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4644 // After analyzing the VAD, set it to NULL so that we don't
4645 // free it in the exit path
4653 // This is case B or case C. First check for case C
4655 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4657 PMEMORY_AREA MemoryArea
;
4660 // This is pretty easy and similar to case A. We compute the
4661 // amount of pages to decommit, update the VAD's commit charge
4662 // and then change the ending address of the VAD to be a bit
4665 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4666 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4670 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4671 // For ReactOS: shrink the corresponding memory area
4672 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4673 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
4674 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
4675 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4676 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
4681 // This is case B and the hardest one. Because we are removing
4682 // a chunk of memory from the very middle of the VAD, we must
4683 // actually split the VAD into two new VADs and compute the
4684 // commit charges for each of them, and reinsert new charges.
4686 // NOT YET IMPLEMENTED IN ARM3.
4688 DPRINT1("Case B not handled\n");
4689 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4694 // After analyzing the VAD, set it to NULL so that we don't
4695 // free it in the exit path
4702 // Now we have a range of pages to dereference, so call the right API
4703 // to do that and then release the working set, since we're done messing
4704 // around with process pages.
4706 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4707 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4708 Status
= STATUS_SUCCESS
;
4712 // Update the process counters
4714 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4715 Process
->CommitCharge
-= CommitReduction
;
4716 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4719 // Unlock the address space and free the VAD in failure cases. Next,
4720 // detach from the target process so we can write the region size and the
4721 // base address to the correct source process, and dereference the target
4724 MmUnlockAddressSpace(AddressSpace
);
4725 if (Vad
) ExFreePool(Vad
);
4726 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4727 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4730 // Use SEH to safely return the region size and the base address of the
4731 // deallocation. If we get an access violation, don't return a failure code
4732 // as the deallocation *has* happened. The caller will just have to figure
4733 // out another way to find out where it is (such as VirtualQuery).
4737 *URegionSize
= PRegionSize
;
4738 *UBaseAddress
= (PVOID
)StartingAddress
;
4740 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4748 // This is the decommit path. You cannot decommit from the following VADs in
4749 // Windows, so fail the vall
4751 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4752 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4753 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4755 DPRINT1("Trying to decommit from invalid VAD\n");
4756 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4761 // If the caller did not specify a region size, first make sure that this
4762 // region is actually committed. If it is, then compute the ending address
4763 // based on the VAD.
4767 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4769 DPRINT1("Decomitting non-committed memory\n");
4770 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4773 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4777 // Decommit the PTEs for the range plus the actual backing pages for the
4778 // range, then reduce that amount from the commit charge in the VAD
4780 CommitReduction
= MiAddressToPte(EndingAddress
) -
4781 MiAddressToPte(StartingAddress
) +
4783 MiDecommitPages((PVOID
)StartingAddress
,
4784 MiAddressToPte(EndingAddress
),
4787 ASSERT(CommitReduction
>= 0);
4788 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4789 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4792 // We are done, go to the exit path without freeing the VAD as it remains
4793 // valid since we have not released the allocation.
4796 Status
= STATUS_SUCCESS
;
4800 // In the failure path, we detach and derefernece the target process, and
4801 // return whatever failure code was sent.
4804 MmUnlockAddressSpace(AddressSpace
);
4805 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4806 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);