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 *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
18 #define MI_MAPPED_COPY_PAGES 14
19 #define MI_POOL_COPY_BYTES 512
20 #define MI_MAX_TRANSFER_SIZE 64 * 1024
23 MiProtectVirtualMemory(IN PEPROCESS Process
,
24 IN OUT PVOID
*BaseAddress
,
25 IN OUT PSIZE_T NumberOfBytesToProtect
,
26 IN ULONG NewAccessProtection
,
27 OUT PULONG OldAccessProtection OPTIONAL
);
31 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
33 IN ULONG ProtectionMask
,
35 IN BOOLEAN CaptureDirtyBit
);
38 /* PRIVATE FUNCTIONS **********************************************************/
42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
43 IN ULONG_PTR EndingAddress
,
47 PMMPTE PointerPte
, LastPte
, PointerPde
;
50 /* Compute starting and ending PTE and PDE addresses */
51 PointerPde
= MiAddressToPde(StartingAddress
);
52 PointerPte
= MiAddressToPte(StartingAddress
);
53 LastPte
= MiAddressToPte(EndingAddress
);
55 /* Handle commited pages first */
56 if (Vad
->u
.VadFlags
.MemCommit
== 1)
58 /* This is a committed VAD, so Assume the whole range is committed */
59 CommittedPages
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
61 /* Is the PDE demand-zero? */
62 PointerPde
= MiAddressToPte(PointerPte
);
63 if (PointerPde
->u
.Long
!= 0)
65 /* It is not. Is it valid? */
66 if (PointerPde
->u
.Hard
.Valid
== 0)
69 PointerPte
= MiPteToAddress(PointerPde
);
70 MiMakeSystemAddressValid(PointerPte
, Process
);
75 /* It is, skip it and move to the next PDE, unless we're done */
77 PointerPte
= MiPteToAddress(PointerPde
);
78 if (PointerPte
> LastPte
) return CommittedPages
;
81 /* Now loop all the PTEs in the range */
82 while (PointerPte
<= LastPte
)
84 /* Have we crossed a PDE boundary? */
85 if (MiIsPteOnPdeBoundary(PointerPte
))
87 /* Is this PDE demand zero? */
88 PointerPde
= MiAddressToPte(PointerPte
);
89 if (PointerPde
->u
.Long
!= 0)
91 /* It isn't -- is it valid? */
92 if (PointerPde
->u
.Hard
.Valid
== 0)
94 /* Nope, fault it in */
95 PointerPte
= MiPteToAddress(PointerPde
);
96 MiMakeSystemAddressValid(PointerPte
, Process
);
101 /* It is, skip it and move to the next PDE */
103 PointerPte
= MiPteToAddress(PointerPde
);
108 /* Is this PTE demand zero? */
109 if (PointerPte
->u
.Long
!= 0)
111 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
112 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
113 (PointerPte
->u
.Hard
.Valid
== 0) &&
114 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
115 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
117 /* It is, so remove it from the count of commited pages */
122 /* Move to the next PTE */
126 /* Return how many committed pages there still are */
127 return CommittedPages
;
130 /* This is a non-commited VAD, so assume none of it is committed */
133 /* Is the PDE demand-zero? */
134 PointerPde
= MiAddressToPte(PointerPte
);
135 if (PointerPde
->u
.Long
!= 0)
137 /* It isn't -- is it invalid? */
138 if (PointerPde
->u
.Hard
.Valid
== 0)
140 /* It is, so page it in */
141 PointerPte
= MiPteToAddress(PointerPde
);
142 MiMakeSystemAddressValid(PointerPte
, Process
);
147 /* It is, so skip it and move to the next PDE */
149 PointerPte
= MiPteToAddress(PointerPde
);
150 if (PointerPte
> LastPte
) return CommittedPages
;
153 /* Loop all the PTEs in this PDE */
154 while (PointerPte
<= LastPte
)
156 /* Have we crossed a PDE boundary? */
157 if (MiIsPteOnPdeBoundary(PointerPte
))
159 /* Is this new PDE demand-zero? */
160 PointerPde
= MiAddressToPte(PointerPte
);
161 if (PointerPde
->u
.Long
!= 0)
163 /* It isn't. Is it valid? */
164 if (PointerPde
->u
.Hard
.Valid
== 0)
166 /* It isn't, so make it valid */
167 PointerPte
= MiPteToAddress(PointerPde
);
168 MiMakeSystemAddressValid(PointerPte
, Process
);
173 /* It is, so skip it and move to the next one */
175 PointerPte
= MiPteToAddress(PointerPde
);
180 /* Is this PTE demand-zero? */
181 if (PointerPte
->u
.Long
!= 0)
183 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
184 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
185 (PointerPte
->u
.Hard
.Valid
== 1) ||
186 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
187 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
189 /* It is! So we'll treat this as a committed page */
194 /* Move to the next PTE */
198 /* Return how many committed pages we found in this VAD */
199 return CommittedPages
;
204 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
205 IN PEPROCESS CurrentProcess
)
208 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
209 PETHREAD CurrentThread
= PsGetCurrentThread();
211 /* Must be a non-pool page table, since those are double-mapped already */
212 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
213 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
214 (PageTableVirtualAddress
> MmPagedPoolEnd
));
216 /* Working set lock or PFN lock should be held */
217 ASSERT(KeAreAllApcsDisabled() == TRUE
);
219 /* Check if the page table is valid */
220 while (!MmIsAddressValid(PageTableVirtualAddress
))
222 /* Release the working set lock */
223 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
229 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
230 if (!NT_SUCCESS(Status
))
232 /* This should not fail */
233 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
236 (ULONG_PTR
)CurrentProcess
,
237 (ULONG_PTR
)PageTableVirtualAddress
);
240 /* Lock the working set again */
241 MiLockProcessWorkingSetForFault(CurrentProcess
,
246 /* This flag will be useful later when we do better locking */
250 /* Let caller know what the lock state is */
256 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
260 BOOLEAN LockChange
= FALSE
;
262 /* Must be e kernel address */
263 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
265 /* Check if the page is valid */
266 while (!MmIsAddressValid(VirtualAddress
))
268 /* Release the PFN database */
269 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
272 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
273 if (!NT_SUCCESS(Status
))
275 /* This should not fail */
276 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
280 (ULONG_PTR
)VirtualAddress
);
283 /* This flag will be useful later when we do better locking */
286 /* Lock the PFN database */
287 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
290 /* Let caller know what the lock state is */
296 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
297 IN PFN_NUMBER PageCount
,
299 OUT PPFN_NUMBER ValidPages
)
301 PFN_COUNT ActualPages
= 0;
302 PETHREAD CurrentThread
= PsGetCurrentThread();
304 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
306 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
308 /* Lock the system working set */
309 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
314 /* Make sure there's some data about the page */
315 if (PointerPte
->u
.Long
)
317 /* As always, only handle current ARM3 scenarios */
318 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
319 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
321 /* Normally this is one possibility -- freeing a valid page */
322 if (PointerPte
->u
.Hard
.Valid
)
324 /* Get the page PFN */
325 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
326 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
328 /* Should not have any working set data yet */
329 ASSERT(Pfn1
->u1
.WsIndex
== 0);
331 /* Actual valid, legitimate, pages */
332 if (ValidPages
) (*ValidPages
)++;
334 /* Get the page table entry */
335 PageTableIndex
= Pfn1
->u4
.PteFrame
;
336 Pfn2
= MiGetPfnEntry(PageTableIndex
);
338 /* Lock the PFN database */
339 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
341 /* Delete it the page */
342 MI_SET_PFN_DELETED(Pfn1
);
343 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
345 /* Decrement the page table too */
346 MiDecrementShareCount(Pfn2
, PageTableIndex
);
348 /* Release the PFN database */
349 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
351 /* Destroy the PTE */
352 PointerPte
->u
.Long
= 0;
355 /* Actual legitimate pages */
361 * The only other ARM3 possibility is a demand zero page, which would
362 * mean freeing some of the paged pool pages that haven't even been
363 * touched yet, as part of a larger allocation.
365 * Right now, we shouldn't expect any page file information in the PTE
367 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
369 /* Destroy the PTE */
370 PointerPte
->u
.Long
= 0;
378 /* Release the working set */
379 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
381 /* Flush the entire TLB */
382 KeFlushEntireTb(TRUE
, TRUE
);
390 MiDeletePte(IN PMMPTE PointerPte
,
391 IN PVOID VirtualAddress
,
392 IN PEPROCESS CurrentProcess
,
393 IN PMMPTE PrototypePte
)
397 PFN_NUMBER PageFrameIndex
;
400 /* PFN lock must be held */
401 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
403 /* Capture the PTE */
404 TempPte
= *PointerPte
;
406 /* We only support valid PTEs for now */
407 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
408 if (TempPte
.u
.Hard
.Valid
== 0)
410 /* Invalid PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
415 /* Get the PFN entry */
416 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
417 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
419 /* Check if this is a valid, prototype PTE */
420 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
422 /* Get the PDE and make sure it's faulted in */
423 PointerPde
= MiPteToPde(PointerPte
);
424 if (PointerPde
->u
.Hard
.Valid
== 0)
426 #if (_MI_PAGING_LEVELS == 2)
427 /* Could be paged pool access from a new process -- synchronize the page directories */
428 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
431 /* The PDE must be valid at this point */
432 KeBugCheckEx(MEMORY_MANAGEMENT
,
434 (ULONG_PTR
)PointerPte
,
436 (ULONG_PTR
)VirtualAddress
);
438 #if (_MI_PAGING_LEVELS == 2)
441 /* Drop the share count */
442 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
444 /* Either a fork, or this is the shared user data page */
445 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
447 /* If it's not the shared user page, then crash, since there's no fork() yet */
448 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
449 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
451 /* Must be some sort of memory corruption */
452 KeBugCheckEx(MEMORY_MANAGEMENT
,
454 (ULONG_PTR
)PointerPte
,
455 (ULONG_PTR
)PrototypePte
,
456 (ULONG_PTR
)Pfn1
->PteAddress
);
462 /* Make sure the saved PTE address is valid */
463 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
465 /* The PFN entry is illegal, or invalid */
466 KeBugCheckEx(MEMORY_MANAGEMENT
,
468 (ULONG_PTR
)PointerPte
,
470 (ULONG_PTR
)Pfn1
->PteAddress
);
473 /* There should only be 1 shared reference count */
474 ASSERT(Pfn1
->u2
.ShareCount
== 1);
476 /* Drop the reference on the page table. */
477 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
479 /* Mark the PFN for deletion and dereference what should be the last ref */
480 MI_SET_PFN_DELETED(Pfn1
);
481 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
483 /* We should eventually do this */
484 //CurrentProcess->NumberOfPrivatePages--;
487 /* Destroy the PTE and flush the TLB */
488 PointerPte
->u
.Long
= 0;
494 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
495 IN ULONG_PTR EndingAddress
,
498 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
501 PEPROCESS CurrentProcess
;
503 BOOLEAN AddressGap
= FALSE
;
504 PSUBSECTION Subsection
;
505 PUSHORT UsedPageTableEntries
;
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess
= PsGetCurrentProcess();
512 PointerPde
= MiAddressToPde(Va
);
513 PointerPte
= MiAddressToPte(Va
);
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
518 /* Don't worry about prototypes */
519 PrototypePte
= LastPrototypePte
= NULL
;
523 /* Get the prototype PTE */
524 PrototypePte
= Vad
->FirstPrototypePte
;
525 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
531 /* Loop the PTE for each VA */
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde
->u
.Long
)
537 /* There are gaps in the address space */
540 /* Still no valid PDE, try the next 4MB (or whatever) */
543 /* Update the PTE on this new boundary */
544 PointerPte
= MiPteToAddress(PointerPde
);
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
548 if (Va
> EndingAddress
) return;
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde
->u
.Hard
.Valid
)
554 /* It isn't, so map it in */
555 PointerPte
= MiPteToAddress(PointerPde
);
556 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
561 ASSERT(Va
<= EndingAddress
);
562 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
564 /* Check if this is a section VAD with gaps in it */
565 if ((AddressGap
) && (LastPrototypePte
))
567 /* We need to skip to the next correct prototype PTE */
568 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
570 /* And we need the subsection to skip to the next last prototype PTE */
571 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
575 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
579 /* No more subsections, we are done with prototype PTEs */
584 /* Lock the PFN Database while we delete the PTEs */
585 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
588 /* Capture the PDE and make sure it exists */
589 TempPte
= *PointerPte
;
592 *UsedPageTableEntries
-= 1;
593 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
595 /* Check if the PTE is actually mapped in */
596 if (TempPte
.u
.Long
& 0xFFFFFC01)
598 /* Are we dealing with section VAD? */
599 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
601 /* We need to skip to the next correct prototype PTE */
602 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
604 /* And we need the subsection to skip to the next last prototype PTE */
605 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
609 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
613 /* No more subsections, we are done with prototype PTEs */
618 /* Check for prototype PTE */
619 if ((TempPte
.u
.Hard
.Valid
== 0) &&
620 (TempPte
.u
.Soft
.Prototype
== 1))
623 PointerPte
->u
.Long
= 0;
627 /* Delete the PTE proper */
628 MiDeletePte(PointerPte
,
636 /* The PTE was never mapped, just nuke it here */
637 PointerPte
->u
.Long
= 0;
641 /* Update the address and PTE for it */
646 /* Making sure the PDE is still valid */
647 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
649 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
651 /* The PDE should still be valid at this point */
652 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
654 if (*UsedPageTableEntries
== 0)
656 if (PointerPde
->u
.Long
!= 0)
658 /* Delete the PTE proper */
659 MiDeletePte(PointerPde
,
660 MiPteToAddress(PointerPde
),
666 /* Release the lock and get out if we're done */
667 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
668 if (Va
> EndingAddress
) return;
670 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
671 PointerPde
= MiAddressToPde(Va
);
677 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
678 OUT PBOOLEAN HaveBadAddress
,
679 OUT PULONG_PTR BadAddress
)
681 PEXCEPTION_RECORD ExceptionRecord
;
687 *HaveBadAddress
= FALSE
;
690 // Get the exception record
692 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
695 // Look at the exception code
697 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
698 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
699 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
702 // We can tell the address if we have more than one parameter
704 if (ExceptionRecord
->NumberParameters
> 1)
707 // Return the address
709 *HaveBadAddress
= TRUE
;
710 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
715 // Continue executing the next handler
717 return EXCEPTION_EXECUTE_HANDLER
;
722 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
723 IN PVOID SourceAddress
,
724 IN PEPROCESS TargetProcess
,
725 OUT PVOID TargetAddress
,
726 IN SIZE_T BufferSize
,
727 IN KPROCESSOR_MODE PreviousMode
,
728 OUT PSIZE_T ReturnSize
)
730 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
731 PMDL Mdl
= (PMDL
)MdlBuffer
;
732 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
733 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
734 volatile BOOLEAN PagesLocked
;
735 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
736 volatile PVOID MdlAddress
;
738 BOOLEAN HaveBadAddress
;
739 ULONG_PTR BadAddress
;
740 NTSTATUS Status
= STATUS_SUCCESS
;
744 // Calculate the maximum amount of data to move
746 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
747 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
748 CurrentSize
= TotalSize
;
749 RemainingSize
= BufferSize
;
752 // Loop as long as there is still data
754 while (RemainingSize
> 0)
757 // Check if this transfer will finish everything off
759 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
762 // Attach to the source address space
764 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
767 // Reset state for this pass
771 FailedInMoving
= FALSE
;
772 ASSERT(FailedInProbe
== FALSE
);
775 // Protect user-mode copy
780 // If this is our first time, probe the buffer
782 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
785 // Catch a failure here
787 FailedInProbe
= TRUE
;
792 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
797 FailedInProbe
= FALSE
;
801 // Initialize and probe and lock the MDL
803 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
804 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
810 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
819 // Use our SEH handler to pick this up
821 FailedInMapping
= TRUE
;
822 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
826 // Now let go of the source and grab to the target process
828 KeUnstackDetachProcess(&ApcState
);
829 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
832 // Check if this is our first time through
834 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
837 // Catch a failure here
839 FailedInProbe
= TRUE
;
844 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
849 FailedInProbe
= FALSE
;
853 // Now do the actual move
855 FailedInMoving
= TRUE
;
856 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
858 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
863 // Detach from whoever we may be attached to
865 KeUnstackDetachProcess(&ApcState
);
868 // Check if we had mapped the pages
870 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
873 // Check if we had locked the pages
875 if (PagesLocked
) MmUnlockPages(Mdl
);
878 // Check if we hit working set quota
880 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
885 return STATUS_WORKING_SET_QUOTA
;
889 // Check if we failed during the probe or mapping
891 if ((FailedInProbe
) || (FailedInMapping
))
896 Status
= _SEH2_GetExceptionCode();
897 _SEH2_YIELD(return Status
);
901 // Otherwise, we failed probably during the move
903 *ReturnSize
= BufferSize
- RemainingSize
;
907 // Check if we know exactly where we stopped copying
912 // Return the exact number of bytes copied
914 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
919 // Return partial copy
921 Status
= STATUS_PARTIAL_COPY
;
926 // Check for SEH status
928 if (Status
!= STATUS_SUCCESS
) return Status
;
931 // Detach from target
933 KeUnstackDetachProcess(&ApcState
);
938 MmUnmapLockedPages(MdlAddress
, Mdl
);
942 // Update location and size
944 RemainingSize
-= CurrentSize
;
945 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
946 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
952 *ReturnSize
= BufferSize
;
953 return STATUS_SUCCESS
;
958 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
959 IN PVOID SourceAddress
,
960 IN PEPROCESS TargetProcess
,
961 OUT PVOID TargetAddress
,
962 IN SIZE_T BufferSize
,
963 IN KPROCESSOR_MODE PreviousMode
,
964 OUT PSIZE_T ReturnSize
)
966 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
967 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
968 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
969 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
972 BOOLEAN HaveBadAddress
;
973 ULONG_PTR BadAddress
;
974 NTSTATUS Status
= STATUS_SUCCESS
;
978 // Calculate the maximum amount of data to move
980 TotalSize
= MI_MAX_TRANSFER_SIZE
;
981 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
982 CurrentSize
= TotalSize
;
983 RemainingSize
= BufferSize
;
986 // Check if we can use the stack
988 if (BufferSize
<= MI_POOL_COPY_BYTES
)
993 PoolAddress
= (PVOID
)StackBuffer
;
1000 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1001 if (!PoolAddress
) ASSERT(FALSE
);
1002 HavePoolAddress
= TRUE
;
1006 // Loop as long as there is still data
1008 while (RemainingSize
> 0)
1011 // Check if this transfer will finish everything off
1013 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1016 // Attach to the source address space
1018 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1021 // Reset state for this pass
1023 FailedInMoving
= FALSE
;
1024 ASSERT(FailedInProbe
== FALSE
);
1027 // Protect user-mode copy
1032 // If this is our first time, probe the buffer
1034 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1037 // Catch a failure here
1039 FailedInProbe
= TRUE
;
1044 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1049 FailedInProbe
= FALSE
;
1055 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1058 // Now let go of the source and grab to the target process
1060 KeUnstackDetachProcess(&ApcState
);
1061 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1064 // Check if this is our first time through
1066 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1069 // Catch a failure here
1071 FailedInProbe
= TRUE
;
1076 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1081 FailedInProbe
= FALSE
;
1085 // Now do the actual move
1087 FailedInMoving
= TRUE
;
1088 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1090 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1095 // Detach from whoever we may be attached to
1097 KeUnstackDetachProcess(&ApcState
);
1100 // Check if we had allocated pool
1102 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1105 // Check if we failed during the probe
1112 Status
= _SEH2_GetExceptionCode();
1113 _SEH2_YIELD(return Status
);
1117 // Otherwise, we failed, probably during the move
1119 *ReturnSize
= BufferSize
- RemainingSize
;
1123 // Check if we know exactly where we stopped copying
1128 // Return the exact number of bytes copied
1130 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1135 // Return partial copy
1137 Status
= STATUS_PARTIAL_COPY
;
1142 // Check for SEH status
1144 if (Status
!= STATUS_SUCCESS
) return Status
;
1147 // Detach from target
1149 KeUnstackDetachProcess(&ApcState
);
1152 // Update location and size
1154 RemainingSize
-= CurrentSize
;
1155 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1156 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1161 // Check if we had allocated pool
1163 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1168 *ReturnSize
= BufferSize
;
1169 return STATUS_SUCCESS
;
1174 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1175 IN PVOID SourceAddress
,
1176 IN PEPROCESS TargetProcess
,
1177 OUT PVOID TargetAddress
,
1178 IN SIZE_T BufferSize
,
1179 IN KPROCESSOR_MODE PreviousMode
,
1180 OUT PSIZE_T ReturnSize
)
1183 PEPROCESS Process
= SourceProcess
;
1186 // Don't accept zero-sized buffers
1188 if (!BufferSize
) return STATUS_SUCCESS
;
1191 // If we are copying from ourselves, lock the target instead
1193 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1196 // Acquire rundown protection
1198 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1203 return STATUS_PROCESS_IS_TERMINATING
;
1207 // See if we should use the pool copy
1209 if (BufferSize
> MI_POOL_COPY_BYTES
)
1214 Status
= MiDoMappedCopy(SourceProcess
,
1227 Status
= MiDoPoolCopy(SourceProcess
,
1239 ExReleaseRundownProtection(&Process
->RundownProtect
);
1245 MmFlushVirtualMemory(IN PEPROCESS Process
,
1246 IN OUT PVOID
*BaseAddress
,
1247 IN OUT PSIZE_T RegionSize
,
1248 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1256 return STATUS_SUCCESS
;
1261 MiGetPageProtection(IN PMMPTE PointerPte
)
1267 /* Copy this PTE's contents */
1268 TempPte
= *PointerPte
;
1270 /* Assure it's not totally zero */
1271 ASSERT(TempPte
.u
.Long
);
1273 /* Check for a special prototype format */
1274 if (TempPte
.u
.Soft
.Valid
== 0 &&
1275 TempPte
.u
.Soft
.Prototype
== 1)
1277 /* Unsupported now */
1282 /* In the easy case of transition or demand zero PTE just return its protection */
1283 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1285 /* If we get here, the PTE is valid, so look up the page in PFN database */
1286 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1287 if (!Pfn
->u3
.e1
.PrototypePte
)
1289 /* Return protection of the original pte */
1290 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1291 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1294 /* This is software PTE */
1295 DPRINT1("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1296 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte
));
1297 DPRINT1("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1298 DPRINT1("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1299 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1304 MiQueryAddressState(IN PVOID Va
,
1306 IN PEPROCESS TargetProcess
,
1307 OUT PULONG ReturnedProtect
,
1311 PMMPTE PointerPte
, ProtoPte
;
1313 MMPTE TempPte
, TempProtoPte
;
1314 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1315 ULONG State
= MEM_RESERVE
, Protect
= 0;
1316 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1317 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1319 /* Only normal VADs supported */
1320 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1322 /* Get the PDE and PTE for the address */
1323 PointerPde
= MiAddressToPde(Va
);
1324 PointerPte
= MiAddressToPte(Va
);
1326 /* Return the next range */
1327 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1329 /* Is the PDE demand-zero? */
1330 if (PointerPde
->u
.Long
!= 0)
1332 /* It is not. Is it valid? */
1333 if (PointerPde
->u
.Hard
.Valid
== 0)
1335 /* Is isn't, fault it in */
1336 PointerPte
= MiPteToAddress(PointerPde
);
1337 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1343 /* It is, skip it and move to the next PDE */
1344 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1347 /* Is it safe to try reading the PTE? */
1350 /* FIXME: watch out for large pages */
1351 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1353 /* Capture the PTE */
1354 TempPte
= *PointerPte
;
1355 if (TempPte
.u
.Long
!= 0)
1357 /* The PTE is valid, so it's not zeroed out */
1358 DemandZeroPte
= FALSE
;
1360 /* Is it a decommited, invalid, or faulted PTE? */
1361 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1362 (TempPte
.u
.Hard
.Valid
== 0) &&
1363 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1364 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1366 /* Otherwise our defaults should hold */
1367 ASSERT(Protect
== 0);
1368 ASSERT(State
== MEM_RESERVE
);
1372 /* This means it's committed */
1375 /* We don't support these */
1376 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1377 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1378 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1380 /* Get protection state of this page */
1381 Protect
= MiGetPageProtection(PointerPte
);
1383 /* Check if this is an image-backed VAD */
1384 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1385 (TempPte
.u
.Soft
.Prototype
== 1) &&
1386 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1389 DPRINT1("Not supported\n");
1396 /* Check if this was a demand-zero PTE, since we need to find the state */
1399 /* Not yet handled */
1400 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1401 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1403 /* Check if this is private commited memory, or an section-backed VAD */
1404 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1406 /* Tell caller about the next range */
1407 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1409 /* Get the prototype PTE for this VAD */
1410 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1411 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1414 /* We should unlock the working set, but it's not being held! */
1416 /* Is the prototype PTE actually valid (committed)? */
1417 TempProtoPte
= *ProtoPte
;
1418 if (TempProtoPte
.u
.Long
)
1420 /* Unless this is a memory-mapped file, handle it like private VAD */
1422 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1423 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1426 /* We should re-lock the working set */
1429 else if (Vad
->u
.VadFlags
.MemCommit
)
1431 /* This is committed memory */
1434 /* Convert the protection */
1435 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1439 /* Return the protection code */
1440 *ReturnedProtect
= Protect
;
1446 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1447 IN PVOID BaseAddress
,
1448 OUT PVOID MemoryInformation
,
1449 IN SIZE_T MemoryInformationLength
,
1450 OUT PSIZE_T ReturnLength
)
1452 PEPROCESS TargetProcess
;
1453 NTSTATUS Status
= STATUS_SUCCESS
;
1455 PVOID Address
, NextAddress
;
1456 BOOLEAN Found
= FALSE
;
1457 ULONG NewProtect
, NewState
;
1459 MEMORY_BASIC_INFORMATION MemoryInfo
;
1460 KAPC_STATE ApcState
;
1461 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1462 PMEMORY_AREA MemoryArea
;
1463 SIZE_T ResultLength
;
1465 /* Check for illegal addresses in user-space, or the shared memory area */
1466 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1467 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1469 Address
= PAGE_ALIGN(BaseAddress
);
1471 /* Make up an info structure describing this range */
1472 MemoryInfo
.BaseAddress
= Address
;
1473 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1474 MemoryInfo
.Type
= MEM_PRIVATE
;
1476 /* Special case for shared data */
1477 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1479 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1480 MemoryInfo
.State
= MEM_COMMIT
;
1481 MemoryInfo
.Protect
= PAGE_READONLY
;
1482 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1486 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1487 MemoryInfo
.State
= MEM_RESERVE
;
1488 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1489 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1492 /* Return the data, NtQueryInformation already probed it*/
1493 if (PreviousMode
!= KernelMode
)
1497 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1498 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1500 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1502 Status
= _SEH2_GetExceptionCode();
1508 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1509 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1515 /* Check if this is for a local or remote process */
1516 if (ProcessHandle
== NtCurrentProcess())
1518 TargetProcess
= PsGetCurrentProcess();
1522 /* Reference the target process */
1523 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1524 PROCESS_QUERY_INFORMATION
,
1526 ExGetPreviousMode(),
1527 (PVOID
*)&TargetProcess
,
1529 if (!NT_SUCCESS(Status
)) return Status
;
1531 /* Attach to it now */
1532 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1536 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1537 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1539 /* Scan on the right */
1540 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1541 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1544 /* Check if this VAD covers the allocation range */
1545 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1546 (BaseVpn
<= Vad
->EndingVpn
))
1553 /* Check if this VAD is too high */
1554 if (BaseVpn
< Vad
->StartingVpn
)
1556 /* Stop if there is no left child */
1557 if (!Vad
->LeftChild
) break;
1559 /* Search on the left next */
1560 Vad
= Vad
->LeftChild
;
1564 /* Then this VAD is too low, keep searching on the right */
1565 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1567 /* Stop if there is no right child */
1568 if (!Vad
->RightChild
) break;
1570 /* Search on the right next */
1571 Vad
= Vad
->RightChild
;
1576 /* Was a VAD found? */
1579 Address
= PAGE_ALIGN(BaseAddress
);
1581 /* Calculate region size */
1584 if (Vad
->StartingVpn
>= BaseVpn
)
1586 /* Region size is the free space till the start of that VAD */
1587 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1591 /* Get the next VAD */
1592 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1595 /* Region size is the free space till the start of that VAD */
1596 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1600 /* Maximum possible region size with that base address */
1601 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1607 /* Maximum possible region size with that base address */
1608 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1611 /* Check if we were attached */
1612 if (ProcessHandle
!= NtCurrentProcess())
1614 /* Detach and derefernece the process */
1615 KeUnstackDetachProcess(&ApcState
);
1616 ObDereferenceObject(TargetProcess
);
1619 /* Build the rest of the initial information block */
1620 MemoryInfo
.BaseAddress
= Address
;
1621 MemoryInfo
.AllocationBase
= NULL
;
1622 MemoryInfo
.AllocationProtect
= 0;
1623 MemoryInfo
.State
= MEM_FREE
;
1624 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1625 MemoryInfo
.Type
= 0;
1627 /* Return the data, NtQueryInformation already probed it*/
1628 if (PreviousMode
!= KernelMode
)
1632 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1633 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1635 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1637 Status
= _SEH2_GetExceptionCode();
1643 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1644 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1650 /* Set the correct memory type based on what kind of VAD this is */
1651 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1652 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1654 MemoryInfo
.Type
= MEM_PRIVATE
;
1656 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1658 MemoryInfo
.Type
= MEM_IMAGE
;
1662 MemoryInfo
.Type
= MEM_MAPPED
;
1665 /* Lock the address space of the process */
1666 MmLockAddressSpace(&TargetProcess
->Vm
);
1668 /* Find the memory area the specified address belongs to */
1669 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1670 ASSERT(MemoryArea
!= NULL
);
1672 /* Determine information dependent on the memory area type */
1673 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1675 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1676 ASSERT(NT_SUCCESS(Status
));
1680 /* Build the initial information block */
1681 Address
= PAGE_ALIGN(BaseAddress
);
1682 MemoryInfo
.BaseAddress
= Address
;
1683 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1684 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1685 MemoryInfo
.Type
= MEM_PRIVATE
;
1687 /* Find the largest chunk of memory which has the same state and protection mask */
1688 MemoryInfo
.State
= MiQueryAddressState(Address
,
1691 &MemoryInfo
.Protect
,
1693 Address
= NextAddress
;
1694 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1696 /* Keep going unless the state or protection mask changed */
1697 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1698 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1699 Address
= NextAddress
;
1702 /* Now that we know the last VA address, calculate the region size */
1703 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1706 /* Unlock the address space of the process */
1707 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1709 /* Check if we were attached */
1710 if (ProcessHandle
!= NtCurrentProcess())
1712 /* Detach and derefernece the process */
1713 KeUnstackDetachProcess(&ApcState
);
1714 ObDereferenceObject(TargetProcess
);
1717 /* Return the data, NtQueryInformation already probed it*/
1718 if (PreviousMode
!= KernelMode
)
1722 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1723 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1725 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1727 Status
= _SEH2_GetExceptionCode();
1733 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1734 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1738 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1739 "State: %lx Type: %lx Size: %lx\n",
1740 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1741 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1742 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1749 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1750 IN ULONG_PTR EndingAddress
,
1752 IN PEPROCESS Process
)
1754 PMMPTE PointerPte
, LastPte
, PointerPde
;
1755 BOOLEAN OnBoundary
= TRUE
;
1758 /* Get the PDE and PTE addresses */
1759 PointerPde
= MiAddressToPde(StartingAddress
);
1760 PointerPte
= MiAddressToPte(StartingAddress
);
1761 LastPte
= MiAddressToPte(EndingAddress
);
1763 /* Loop all the PTEs */
1764 while (PointerPte
<= LastPte
)
1766 /* Check if we've hit an new PDE boundary */
1769 /* Is this PDE demand zero? */
1770 PointerPde
= MiAddressToPte(PointerPte
);
1771 if (PointerPde
->u
.Long
!= 0)
1773 /* It isn't -- is it valid? */
1774 if (PointerPde
->u
.Hard
.Valid
== 0)
1776 /* Nope, fault it in */
1777 PointerPte
= MiPteToAddress(PointerPde
);
1778 MiMakeSystemAddressValid(PointerPte
, Process
);
1783 /* The PTE was already valid, so move to the next one */
1785 PointerPte
= MiPteToAddress(PointerPde
);
1787 /* Is the entire VAD committed? If not, fail */
1788 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1790 /* Everything is committed so far past the range, return true */
1791 if (PointerPte
> LastPte
) return TRUE
;
1795 /* Is the PTE demand zero? */
1796 if (PointerPte
->u
.Long
== 0)
1798 /* Is the entire VAD committed? If not, fail */
1799 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1803 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1804 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1805 (PointerPte
->u
.Hard
.Valid
== 0) &&
1806 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1807 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1809 /* Then part of the range is decommitted, so fail */
1814 /* Move to the next PTE */
1816 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1819 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1825 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1826 IN OUT PVOID
*BaseAddress
,
1827 IN OUT PSIZE_T NumberOfBytesToProtect
,
1828 IN ULONG NewAccessProtection
,
1829 OUT PULONG OldAccessProtection OPTIONAL
)
1831 PMEMORY_AREA MemoryArea
;
1832 PMMSUPPORT AddressSpace
;
1833 ULONG OldAccessProtection_
;
1836 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1837 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1839 AddressSpace
= &Process
->Vm
;
1840 MmLockAddressSpace(AddressSpace
);
1841 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
1842 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
1844 MmUnlockAddressSpace(AddressSpace
);
1845 return STATUS_UNSUCCESSFUL
;
1848 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
1850 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
1851 Status
= MmProtectSectionView(AddressSpace
,
1854 *NumberOfBytesToProtect
,
1855 NewAccessProtection
,
1856 OldAccessProtection
);
1858 MmUnlockAddressSpace(AddressSpace
);
1865 MiProtectVirtualMemory(IN PEPROCESS Process
,
1866 IN OUT PVOID
*BaseAddress
,
1867 IN OUT PSIZE_T NumberOfBytesToProtect
,
1868 IN ULONG NewAccessProtection
,
1869 OUT PULONG OldAccessProtection OPTIONAL
)
1871 PMEMORY_AREA MemoryArea
;
1873 PMMSUPPORT AddressSpace
;
1874 ULONG_PTR StartingAddress
, EndingAddress
;
1875 PMMPTE PointerPde
, PointerPte
, LastPte
;
1878 ULONG ProtectionMask
, OldProtect
;
1880 NTSTATUS Status
= STATUS_SUCCESS
;
1881 PETHREAD Thread
= PsGetCurrentThread();
1883 /* Calculate base address for the VAD */
1884 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1885 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1887 /* Calculate the protection mask and make sure it's valid */
1888 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1889 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1891 DPRINT1("Invalid protection mask\n");
1892 return STATUS_INVALID_PAGE_PROTECTION
;
1895 /* Check for ROS specific memory area */
1896 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1897 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1900 return MiRosProtectVirtualMemory(Process
,
1902 NumberOfBytesToProtect
,
1903 NewAccessProtection
,
1904 OldAccessProtection
);
1907 /* Lock the address space and make sure the process isn't already dead */
1908 AddressSpace
= MmGetCurrentAddressSpace();
1909 MmLockAddressSpace(AddressSpace
);
1910 if (Process
->VmDeleted
)
1912 DPRINT1("Process is dying\n");
1913 Status
= STATUS_PROCESS_IS_TERMINATING
;
1917 /* Get the VAD for this address range, and make sure it exists */
1918 Vad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
1919 EndingAddress
>> PAGE_SHIFT
,
1923 DPRINT("Could not find a VAD for this allocation\n");
1924 Status
= STATUS_CONFLICTING_ADDRESSES
;
1928 /* Make sure the address is within this VAD's boundaries */
1929 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
1930 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
1932 Status
= STATUS_CONFLICTING_ADDRESSES
;
1936 /* These kinds of VADs are not supported atm */
1937 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
1938 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
1939 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
1941 DPRINT1("Illegal VAD for attempting to set protection\n");
1942 Status
= STATUS_CONFLICTING_ADDRESSES
;
1946 /* Check for a VAD whose protection can't be changed */
1947 if (Vad
->u
.VadFlags
.NoChange
== 1)
1949 DPRINT1("Trying to change protection of a NoChange VAD\n");
1950 Status
= STATUS_INVALID_PAGE_PROTECTION
;
1954 /* Is this section, or private memory? */
1955 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
1957 /* Not yet supported */
1958 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
1960 DPRINT1("Illegal VAD for attempting to set protection\n");
1961 Status
= STATUS_CONFLICTING_ADDRESSES
;
1965 /* Rotate VADs are not yet supported */
1966 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
1968 DPRINT1("Illegal VAD for attempting to set protection\n");
1969 Status
= STATUS_CONFLICTING_ADDRESSES
;
1973 /* Not valid on section files */
1974 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
1977 DPRINT1("Invalid protection flags for section\n");
1978 Status
= STATUS_INVALID_PARAMETER_4
;
1982 /* Check if data or page file mapping protection PTE is compatible */
1983 if (!Vad
->ControlArea
->u
.Flags
.Image
)
1986 DPRINT1("Fixme: Not checking for valid protection\n");
1989 /* This is a section, and this is not yet supported */
1990 DPRINT1("Section protection not yet supported\n");
1995 /* Private memory, check protection flags */
1996 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
1997 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
1999 DPRINT1("Invalid protection flags for private memory\n");
2000 Status
= STATUS_INVALID_PARAMETER_4
;
2004 /* Lock the working set */
2005 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2007 /* Check if all pages in this range are committed */
2008 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2015 DPRINT1("The entire range is not committed\n");
2016 Status
= STATUS_NOT_COMMITTED
;
2017 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2021 /* Compute starting and ending PTE and PDE addresses */
2022 PointerPde
= MiAddressToPde(StartingAddress
);
2023 PointerPte
= MiAddressToPte(StartingAddress
);
2024 LastPte
= MiAddressToPte(EndingAddress
);
2026 /* Make this PDE valid */
2027 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2029 /* Save protection of the first page */
2030 if (PointerPte
->u
.Long
!= 0)
2032 /* Capture the page protection and make the PDE valid */
2033 OldProtect
= MiGetPageProtection(PointerPte
);
2034 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2038 /* Grab the old protection from the VAD itself */
2039 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2042 /* Loop all the PTEs now */
2043 while (PointerPte
<= LastPte
)
2045 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2046 if (MiIsPteOnPdeBoundary(PointerPte
))
2048 PointerPde
= MiAddressToPte(PointerPte
);
2049 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2052 /* Capture the PTE and check if it was empty */
2053 PteContents
= *PointerPte
;
2054 if (PteContents
.u
.Long
== 0)
2056 /* This used to be a zero PTE and it no longer is, so we must add a
2057 reference to the pagetable. */
2058 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2061 /* Check what kind of PTE we are dealing with */
2062 if (PteContents
.u
.Hard
.Valid
== 1)
2064 /* Get the PFN entry */
2065 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2067 /* We don't support this yet */
2068 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2070 /* Check if the page should not be accessible at all */
2071 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2072 (NewAccessProtection
& PAGE_GUARD
))
2074 /* The page should be in the WS and we should make it transition now */
2075 DPRINT1("Making valid page invalid is not yet supported!\n");
2076 Status
= STATUS_NOT_IMPLEMENTED
;
2077 /* Unlock the working set */
2078 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2082 /* Write the protection mask and write it with a TLB flush */
2083 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2084 MiFlushTbAndCapture(Vad
,
2092 /* We don't support these cases yet */
2093 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2094 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2096 /* The PTE is already demand-zero, just update the protection mask */
2097 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
2098 ASSERT(PointerPte
->u
.Long
!= 0);
2101 /* Move to the next PTE */
2105 /* Unlock the working set */
2106 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2109 /* Unlock the address space */
2110 MmUnlockAddressSpace(AddressSpace
);
2112 /* Return parameters and success */
2113 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2114 *BaseAddress
= (PVOID
)StartingAddress
;
2115 *OldAccessProtection
= OldProtect
;
2116 return STATUS_SUCCESS
;
2119 /* Unlock the address space and return the failure code */
2120 MmUnlockAddressSpace(AddressSpace
);
2126 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2127 IN PEPROCESS TargetProcess
,
2130 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2133 // Sanity checks. The latter is because we only use this function with the
2134 // PFN lock not held, so it may go away in the future.
2136 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2137 ASSERT(OldIrql
== MM_NOIRQL
);
2140 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2141 // return the same address as the PDE on 2-level page table systems.
2143 // If everything is already valid, there is nothing to do.
2145 PointerPpe
= MiAddressToPte(PointerPde
);
2146 PointerPxe
= MiAddressToPde(PointerPde
);
2147 if ((PointerPxe
->u
.Hard
.Valid
) &&
2148 (PointerPpe
->u
.Hard
.Valid
) &&
2149 (PointerPde
->u
.Hard
.Valid
))
2155 // At least something is invalid, so begin by getting the PTE for the PDE itself
2156 // and then lookup each additional level. We must do it in this precise order
2157 // because the pagfault.c code (as well as in Windows) depends that the next
2158 // level up (higher) must be valid when faulting a lower level
2160 PointerPte
= MiPteToAddress(PointerPde
);
2164 // Make sure APCs continued to be disabled
2166 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2169 // First, make the PXE valid if needed
2171 if (!PointerPxe
->u
.Hard
.Valid
)
2173 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2174 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2180 if (!PointerPpe
->u
.Hard
.Valid
)
2182 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2183 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2187 // And finally, make the PDE itself valid.
2189 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2192 // This should've worked the first time so the loop is really just for
2193 // show -- ASSERT that we're actually NOT going to be looping.
2195 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2196 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2197 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2198 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2199 !(PointerPpe
->u
.Hard
.Valid
) ||
2200 !(PointerPde
->u
.Hard
.Valid
));
2205 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2211 PFN_NUMBER PageFrameIndex
;
2215 // Acquire the PFN lock and loop all the PTEs in the list
2217 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2218 for (i
= 0; i
!= Count
; i
++)
2221 // The PTE must currently be valid
2223 TempPte
= *ValidPteList
[i
];
2224 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2227 // Get the PFN entry for the page itself, and then for its page table
2229 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2230 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2231 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2234 // Decrement the share count on the page table, and then on the page
2237 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2238 MI_SET_PFN_DELETED(Pfn1
);
2239 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2242 // Make the page decommitted
2244 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2248 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2249 // and then release the PFN lock
2252 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2257 MiDecommitPages(IN PVOID StartingAddress
,
2258 IN PMMPTE EndingPte
,
2259 IN PEPROCESS Process
,
2262 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2263 ULONG CommitReduction
= 0;
2264 PMMPTE ValidPteList
[256];
2268 PETHREAD CurrentThread
= PsGetCurrentThread();
2271 // Get the PTE and PTE for the address, and lock the working set
2272 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2273 // commited range ends so that we can do the right accounting.
2275 PointerPde
= MiAddressToPde(StartingAddress
);
2276 PointerPte
= MiAddressToPte(StartingAddress
);
2277 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2278 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2281 // Make the PDE valid, and now loop through each page's worth of data
2283 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2284 while (PointerPte
<= EndingPte
)
2287 // Check if we've crossed a PDE boundary
2289 if (MiIsPteOnPdeBoundary(PointerPte
))
2292 // Get the new PDE and flush the valid PTEs we had built up until
2293 // now. This helps reduce the amount of TLB flushing we have to do.
2294 // Note that Windows does a much better job using timestamps and
2295 // such, and does not flush the entire TLB all the time, but right
2296 // now we have bigger problems to worry about than TLB flushing.
2298 PointerPde
= MiAddressToPde(StartingAddress
);
2301 MiProcessValidPteList(ValidPteList
, PteCount
);
2306 // Make this PDE valid
2308 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2312 // Read this PTE. It might be active or still demand-zero.
2314 PteContents
= *PointerPte
;
2315 if (PteContents
.u
.Long
)
2318 // The PTE is active. It might be valid and in a working set, or
2319 // it might be a prototype PTE or paged out or even in transition.
2321 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2324 // It's already decommited, so there's nothing for us to do here
2331 // Remove it from the counters, and check if it was valid or not
2333 //Process->NumberOfPrivatePages--;
2334 if (PteContents
.u
.Hard
.Valid
)
2337 // It's valid. At this point make sure that it is not a ROS
2338 // PFN. Also, we don't support ProtoPTEs in this code path.
2340 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2341 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2342 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2345 // Flush any pending PTEs that we had not yet flushed, if our
2346 // list has gotten too big, then add this PTE to the flush list.
2348 if (PteCount
== 256)
2350 MiProcessValidPteList(ValidPteList
, PteCount
);
2353 ValidPteList
[PteCount
++] = PointerPte
;
2358 // We do not support any of these other scenarios at the moment
2360 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2361 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2362 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2365 // So the only other possibility is that it is still a demand
2366 // zero PTE, in which case we undo the accounting we did
2367 // earlier and simply make the page decommitted.
2369 //Process->NumberOfPrivatePages++;
2370 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2377 // This used to be a zero PTE and it no longer is, so we must add a
2378 // reference to the pagetable.
2380 MiIncrementPageTableReferences(StartingAddress
);
2383 // Next, we account for decommitted PTEs and make the PTE as such
2385 if (PointerPte
> CommitPte
) CommitReduction
++;
2386 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2390 // Move to the next PTE and the next address
2393 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2397 // Flush any dangling PTEs from the loop in the last page table, and then
2398 // release the working set and return the commit reduction accounting.
2400 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2401 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2402 return CommitReduction
;
2405 /* PUBLIC FUNCTIONS ***********************************************************/
2412 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2423 MmSecureVirtualMemory(IN PVOID Address
,
2427 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2436 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2438 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2441 /* SYSTEM CALLS ***************************************************************/
2445 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2446 IN PVOID BaseAddress
,
2448 IN SIZE_T NumberOfBytesToRead
,
2449 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2451 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2453 NTSTATUS Status
= STATUS_SUCCESS
;
2454 SIZE_T BytesRead
= 0;
2458 // Check if we came from user mode
2460 if (PreviousMode
!= KernelMode
)
2463 // Validate the read addresses
2465 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2466 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2467 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2468 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2471 // Don't allow to write into kernel space
2473 return STATUS_ACCESS_VIOLATION
;
2477 // Enter SEH for probe
2482 // Probe the output value
2484 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2486 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2489 // Get exception code
2491 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2497 // Don't do zero-byte transfers
2499 if (NumberOfBytesToRead
)
2502 // Reference the process
2504 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2510 if (NT_SUCCESS(Status
))
2515 Status
= MmCopyVirtualMemory(Process
,
2517 PsGetCurrentProcess(),
2519 NumberOfBytesToRead
,
2524 // Dereference the process
2526 ObDereferenceObject(Process
);
2531 // Check if the caller sent this parameter
2533 if (NumberOfBytesRead
)
2536 // Enter SEH to guard write
2541 // Return the number of bytes read
2543 *NumberOfBytesRead
= BytesRead
;
2545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2559 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2560 IN PVOID BaseAddress
,
2562 IN SIZE_T NumberOfBytesToWrite
,
2563 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2565 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2567 NTSTATUS Status
= STATUS_SUCCESS
;
2568 SIZE_T BytesWritten
= 0;
2572 // Check if we came from user mode
2574 if (PreviousMode
!= KernelMode
)
2577 // Validate the read addresses
2579 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2580 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2581 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2582 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2585 // Don't allow to write into kernel space
2587 return STATUS_ACCESS_VIOLATION
;
2591 // Enter SEH for probe
2596 // Probe the output value
2598 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2600 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2603 // Get exception code
2605 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2611 // Don't do zero-byte transfers
2613 if (NumberOfBytesToWrite
)
2616 // Reference the process
2618 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2624 if (NT_SUCCESS(Status
))
2629 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2633 NumberOfBytesToWrite
,
2638 // Dereference the process
2640 ObDereferenceObject(Process
);
2645 // Check if the caller sent this parameter
2647 if (NumberOfBytesWritten
)
2650 // Enter SEH to guard write
2655 // Return the number of bytes written
2657 *NumberOfBytesWritten
= BytesWritten
;
2659 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2673 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2674 IN OUT PVOID
*UnsafeBaseAddress
,
2675 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2676 IN ULONG NewAccessProtection
,
2677 OUT PULONG UnsafeOldAccessProtection
)
2680 ULONG OldAccessProtection
;
2682 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2683 PVOID BaseAddress
= NULL
;
2684 SIZE_T NumberOfBytesToProtect
= 0;
2685 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2687 BOOLEAN Attached
= FALSE
;
2688 KAPC_STATE ApcState
;
2692 // Check for valid protection flags
2694 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2695 if (Protection
!= PAGE_NOACCESS
&&
2696 Protection
!= PAGE_READONLY
&&
2697 Protection
!= PAGE_READWRITE
&&
2698 Protection
!= PAGE_WRITECOPY
&&
2699 Protection
!= PAGE_EXECUTE
&&
2700 Protection
!= PAGE_EXECUTE_READ
&&
2701 Protection
!= PAGE_EXECUTE_READWRITE
&&
2702 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2707 return STATUS_INVALID_PAGE_PROTECTION
;
2711 // Check if we came from user mode
2713 if (PreviousMode
!= KernelMode
)
2716 // Enter SEH for probing
2721 // Validate all outputs
2723 ProbeForWritePointer(UnsafeBaseAddress
);
2724 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2725 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2730 BaseAddress
= *UnsafeBaseAddress
;
2731 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2733 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2736 // Get exception code
2738 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2747 BaseAddress
= *UnsafeBaseAddress
;
2748 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2752 // Catch illegal base address
2754 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2757 // Catch illegal region size
2759 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2764 return STATUS_INVALID_PARAMETER_3
;
2768 // 0 is also illegal
2770 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2773 // Get a reference to the process
2775 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2776 PROCESS_VM_OPERATION
,
2781 if (!NT_SUCCESS(Status
)) return Status
;
2784 // Check if we should attach
2786 if (CurrentProcess
!= Process
)
2791 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2796 // Do the actual work
2798 Status
= MiProtectVirtualMemory(Process
,
2800 &NumberOfBytesToProtect
,
2801 NewAccessProtection
,
2802 &OldAccessProtection
);
2807 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2810 // Release reference
2812 ObDereferenceObject(Process
);
2815 // Enter SEH to return data
2820 // Return data to user
2822 *UnsafeOldAccessProtection
= OldAccessProtection
;
2823 *UnsafeBaseAddress
= BaseAddress
;
2824 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2826 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2839 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2840 IN OUT PVOID
*BaseAddress
,
2841 IN OUT PSIZE_T NumberOfBytesToLock
,
2845 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2847 BOOLEAN Attached
= FALSE
;
2848 KAPC_STATE ApcState
;
2849 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2850 PVOID CapturedBaseAddress
;
2851 SIZE_T CapturedBytesToLock
;
2857 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2860 // Invalid set of flags
2862 return STATUS_INVALID_PARAMETER
;
2866 // At least one flag must be specified
2868 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2873 return STATUS_INVALID_PARAMETER
;
2877 // Enter SEH for probing
2882 // Validate output data
2884 ProbeForWritePointer(BaseAddress
);
2885 ProbeForWriteSize_t(NumberOfBytesToLock
);
2890 CapturedBaseAddress
= *BaseAddress
;
2891 CapturedBytesToLock
= *NumberOfBytesToLock
;
2893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2896 // Get exception code
2898 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2903 // Catch illegal base address
2905 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2908 // Catch illegal region size
2910 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2915 return STATUS_INVALID_PARAMETER
;
2919 // 0 is also illegal
2921 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2924 // Get a reference to the process
2926 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2927 PROCESS_VM_OPERATION
,
2932 if (!NT_SUCCESS(Status
)) return Status
;
2935 // Check if this is is system-mapped
2937 if (MapType
& MAP_SYSTEM
)
2940 // Check for required privilege
2942 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2945 // Fail: Don't have it
2947 ObDereferenceObject(Process
);
2948 return STATUS_PRIVILEGE_NOT_HELD
;
2953 // Check if we should attach
2955 if (CurrentProcess
!= Process
)
2960 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2972 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2975 // Release reference
2977 ObDereferenceObject(Process
);
2980 // Enter SEH to return data
2985 // Return data to user
2987 *BaseAddress
= CapturedBaseAddress
;
2988 *NumberOfBytesToLock
= 0;
2990 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2993 // Get exception code
2995 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3002 return STATUS_SUCCESS
;
3007 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3008 IN OUT PVOID
*BaseAddress
,
3009 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3013 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3015 BOOLEAN Attached
= FALSE
;
3016 KAPC_STATE ApcState
;
3017 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3018 PVOID CapturedBaseAddress
;
3019 SIZE_T CapturedBytesToUnlock
;
3025 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3028 // Invalid set of flags
3030 return STATUS_INVALID_PARAMETER
;
3034 // At least one flag must be specified
3036 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3041 return STATUS_INVALID_PARAMETER
;
3045 // Enter SEH for probing
3050 // Validate output data
3052 ProbeForWritePointer(BaseAddress
);
3053 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3058 CapturedBaseAddress
= *BaseAddress
;
3059 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3061 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3064 // Get exception code
3066 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3071 // Catch illegal base address
3073 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3076 // Catch illegal region size
3078 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3083 return STATUS_INVALID_PARAMETER
;
3087 // 0 is also illegal
3089 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3092 // Get a reference to the process
3094 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3095 PROCESS_VM_OPERATION
,
3100 if (!NT_SUCCESS(Status
)) return Status
;
3103 // Check if this is is system-mapped
3105 if (MapType
& MAP_SYSTEM
)
3108 // Check for required privilege
3110 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3113 // Fail: Don't have it
3115 ObDereferenceObject(Process
);
3116 return STATUS_PRIVILEGE_NOT_HELD
;
3121 // Check if we should attach
3123 if (CurrentProcess
!= Process
)
3128 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3140 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3143 // Release reference
3145 ObDereferenceObject(Process
);
3148 // Enter SEH to return data
3153 // Return data to user
3155 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3156 *NumberOfBytesToUnlock
= 0;
3158 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3161 // Get exception code
3163 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3170 return STATUS_SUCCESS
;
3175 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3176 IN OUT PVOID
*BaseAddress
,
3177 IN OUT PSIZE_T NumberOfBytesToFlush
,
3178 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3182 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3183 PVOID CapturedBaseAddress
;
3184 SIZE_T CapturedBytesToFlush
;
3185 IO_STATUS_BLOCK LocalStatusBlock
;
3189 // Check if we came from user mode
3191 if (PreviousMode
!= KernelMode
)
3194 // Enter SEH for probing
3199 // Validate all outputs
3201 ProbeForWritePointer(BaseAddress
);
3202 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3203 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3208 CapturedBaseAddress
= *BaseAddress
;
3209 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3211 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3214 // Get exception code
3216 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3225 CapturedBaseAddress
= *BaseAddress
;
3226 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3230 // Catch illegal base address
3232 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3235 // Catch illegal region size
3237 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3242 return STATUS_INVALID_PARAMETER
;
3246 // Get a reference to the process
3248 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3249 PROCESS_VM_OPERATION
,
3254 if (!NT_SUCCESS(Status
)) return Status
;
3259 Status
= MmFlushVirtualMemory(Process
,
3260 &CapturedBaseAddress
,
3261 &CapturedBytesToFlush
,
3265 // Release reference
3267 ObDereferenceObject(Process
);
3270 // Enter SEH to return data
3275 // Return data to user
3277 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3278 *NumberOfBytesToFlush
= 0;
3279 *IoStatusBlock
= LocalStatusBlock
;
3281 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3297 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3299 IN PVOID BaseAddress
,
3300 IN SIZE_T RegionSize
,
3301 IN PVOID
*UserAddressArray
,
3302 OUT PULONG_PTR EntriesInUserAddressArray
,
3303 OUT PULONG Granularity
)
3308 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3309 ULONG_PTR CapturedEntryCount
;
3313 // Check if we came from user mode
3315 if (PreviousMode
!= KernelMode
)
3318 // Enter SEH for probing
3323 // Catch illegal base address
3325 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3328 // Catch illegal region size
3330 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3335 return STATUS_INVALID_PARAMETER_3
;
3339 // Validate all data
3341 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3342 ProbeForWriteUlong(Granularity
);
3347 CapturedEntryCount
= *EntriesInUserAddressArray
;
3350 // Must have a count
3352 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
3355 // Can't be larger than the maximum
3357 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3362 return STATUS_INVALID_PARAMETER_5
;
3366 // Probe the actual array
3368 ProbeForWrite(UserAddressArray
,
3369 CapturedEntryCount
* sizeof(PVOID
),
3372 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3375 // Get exception code
3377 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3386 CapturedEntryCount
= *EntriesInUserAddressArray
;
3387 ASSERT(CapturedEntryCount
!= 0);
3391 // Check if this is a local request
3393 if (ProcessHandle
== NtCurrentProcess())
3396 // No need to reference the process
3398 Process
= PsGetCurrentProcess();
3403 // Reference the target
3405 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3406 PROCESS_VM_OPERATION
,
3411 if (!NT_SUCCESS(Status
)) return Status
;
3415 // Compute the last address and validate it
3417 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3418 if (BaseAddress
> EndAddress
)
3423 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3424 return STATUS_INVALID_PARAMETER_4
;
3433 // Dereference if needed
3435 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3438 // Enter SEH to return data
3443 // Return data to user
3445 *EntriesInUserAddressArray
= 0;
3446 *Granularity
= PAGE_SIZE
;
3448 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3451 // Get exception code
3453 Status
= _SEH2_GetExceptionCode();
3460 return STATUS_SUCCESS
;
3468 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3469 IN PVOID BaseAddress
,
3470 IN SIZE_T RegionSize
)
3475 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3476 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3479 // Catch illegal base address
3481 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3484 // Catch illegal region size
3486 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3491 return STATUS_INVALID_PARAMETER_3
;
3495 // Check if this is a local request
3497 if (ProcessHandle
== NtCurrentProcess())
3500 // No need to reference the process
3502 Process
= PsGetCurrentProcess();
3507 // Reference the target
3509 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3510 PROCESS_VM_OPERATION
,
3515 if (!NT_SUCCESS(Status
)) return Status
;
3519 // Compute the last address and validate it
3521 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3522 if (BaseAddress
> EndAddress
)
3527 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3528 return STATUS_INVALID_PARAMETER_3
;
3537 // Dereference if needed
3539 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3544 return STATUS_SUCCESS
;
3549 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3550 IN PVOID BaseAddress
,
3551 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3552 OUT PVOID MemoryInformation
,
3553 IN SIZE_T MemoryInformationLength
,
3554 OUT PSIZE_T ReturnLength
)
3556 NTSTATUS Status
= STATUS_SUCCESS
;
3557 KPROCESSOR_MODE PreviousMode
;
3559 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3561 /* Bail out if the address is invalid */
3562 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3564 /* Probe return buffer */
3565 PreviousMode
= ExGetPreviousMode();
3566 if (PreviousMode
!= KernelMode
)
3570 ProbeForWrite(MemoryInformation
,
3571 MemoryInformationLength
,
3574 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3576 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3578 Status
= _SEH2_GetExceptionCode();
3582 if (!NT_SUCCESS(Status
))
3588 switch(MemoryInformationClass
)
3590 case MemoryBasicInformation
:
3591 /* Validate the size information of the class */
3592 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3594 /* The size is invalid */
3595 return STATUS_INFO_LENGTH_MISMATCH
;
3597 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3600 MemoryInformationLength
,
3604 case MemorySectionName
:
3605 /* Validate the size information of the class */
3606 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3608 /* The size is invalid */
3609 return STATUS_INFO_LENGTH_MISMATCH
;
3611 Status
= MiQueryMemorySectionName(ProcessHandle
,
3614 MemoryInformationLength
,
3617 case MemoryWorkingSetList
:
3618 case MemoryBasicVlmInformation
:
3620 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3632 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3633 IN OUT PVOID
* UBaseAddress
,
3634 IN ULONG_PTR ZeroBits
,
3635 IN OUT PSIZE_T URegionSize
,
3636 IN ULONG AllocationType
,
3640 PMEMORY_AREA MemoryArea
;
3641 PFN_NUMBER PageCount
;
3642 PMMVAD Vad
, FoundVad
;
3644 PMMSUPPORT AddressSpace
;
3646 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3647 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3648 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3649 PETHREAD CurrentThread
= PsGetCurrentThread();
3650 KAPC_STATE ApcState
;
3651 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3652 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3654 PMMPTE PointerPte
, PointerPde
, LastPte
;
3657 /* Check for valid Zero bits */
3660 DPRINT1("Too many zero bits\n");
3661 return STATUS_INVALID_PARAMETER_3
;
3664 /* Check for valid Allocation Types */
3665 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3666 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3668 DPRINT1("Invalid Allocation Type\n");
3669 return STATUS_INVALID_PARAMETER_5
;
3672 /* Check for at least one of these Allocation Types to be set */
3673 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3675 DPRINT1("No memory allocation base type\n");
3676 return STATUS_INVALID_PARAMETER_5
;
3679 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3680 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3682 DPRINT1("Invalid use of MEM_RESET\n");
3683 return STATUS_INVALID_PARAMETER_5
;
3686 /* Check if large pages are being used */
3687 if (AllocationType
& MEM_LARGE_PAGES
)
3689 /* Large page allocations MUST be committed */
3690 if (!(AllocationType
& MEM_COMMIT
))
3692 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3693 return STATUS_INVALID_PARAMETER_5
;
3696 /* These flags are not allowed with large page allocations */
3697 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3699 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3700 return STATUS_INVALID_PARAMETER_5
;
3704 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3705 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3707 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3708 return STATUS_INVALID_PARAMETER_5
;
3711 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3712 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3714 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3715 return STATUS_INVALID_PARAMETER_5
;
3718 /* Check for valid MEM_PHYSICAL usage */
3719 if (AllocationType
& MEM_PHYSICAL
)
3721 /* Only these flags are allowed with MEM_PHYSIAL */
3722 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3724 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3725 return STATUS_INVALID_PARAMETER_5
;
3728 /* Then make sure PAGE_READWRITE is used */
3729 if (Protect
!= PAGE_READWRITE
)
3731 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3732 return STATUS_INVALID_PARAMETER_6
;
3736 /* Calculate the protection mask and make sure it's valid */
3737 ProtectionMask
= MiMakeProtectionMask(Protect
);
3738 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3740 DPRINT1("Invalid protection mask\n");
3741 return STATUS_INVALID_PAGE_PROTECTION
;
3747 /* Check for user-mode parameters */
3748 if (PreviousMode
!= KernelMode
)
3750 /* Make sure they are writable */
3751 ProbeForWritePointer(UBaseAddress
);
3752 ProbeForWriteUlong(URegionSize
);
3755 /* Capture their values */
3756 PBaseAddress
= *UBaseAddress
;
3757 PRegionSize
= *URegionSize
;
3759 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3761 /* Return the exception code */
3762 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3766 /* Make sure the allocation isn't past the VAD area */
3767 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3769 DPRINT1("Virtual allocation base above User Space\n");
3770 return STATUS_INVALID_PARAMETER_2
;
3773 /* Make sure the allocation wouldn't overflow past the VAD area */
3774 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3776 DPRINT1("Region size would overflow into kernel-memory\n");
3777 return STATUS_INVALID_PARAMETER_4
;
3780 /* Make sure there's a size specified */
3783 DPRINT1("Region size is invalid (zero)\n");
3784 return STATUS_INVALID_PARAMETER_4
;
3788 // If this is for the current process, just use PsGetCurrentProcess
3790 if (ProcessHandle
== NtCurrentProcess())
3792 Process
= CurrentProcess
;
3797 // Otherwise, reference the process with VM rights and attach to it if
3798 // this isn't the current process. We must attach because we'll be touching
3799 // PTEs and PDEs that belong to user-mode memory, and also touching the
3800 // Working Set which is stored in Hyperspace.
3802 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3803 PROCESS_VM_OPERATION
,
3808 if (!NT_SUCCESS(Status
)) return Status
;
3809 if (CurrentProcess
!= Process
)
3811 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3817 // Check for large page allocations and make sure that the required privilege
3818 // is being held, before attempting to handle them.
3820 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3821 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3823 /* Fail without it */
3824 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3825 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3826 goto FailPathNoLock
;
3830 // Fail on the things we don't yet support
3834 DPRINT1("Zero bits not supported\n");
3835 Status
= STATUS_INVALID_PARAMETER
;
3836 goto FailPathNoLock
;
3838 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
3840 DPRINT1("MEM_LARGE_PAGES not supported\n");
3841 Status
= STATUS_INVALID_PARAMETER
;
3842 goto FailPathNoLock
;
3844 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
3846 DPRINT1("MEM_PHYSICAL not supported\n");
3847 Status
= STATUS_INVALID_PARAMETER
;
3848 goto FailPathNoLock
;
3850 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
3852 DPRINT1("MEM_WRITE_WATCH not supported\n");
3853 Status
= STATUS_INVALID_PARAMETER
;
3854 goto FailPathNoLock
;
3856 if ((AllocationType
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
)
3858 DPRINT1("MEM_TOP_DOWN not supported\n");
3859 Status
= STATUS_INVALID_PARAMETER
;
3860 goto FailPathNoLock
;
3862 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
3864 /// @todo HACK: pretend success
3865 DPRINT("MEM_RESET not supported\n");
3866 Status
= STATUS_SUCCESS
;
3867 goto FailPathNoLock
;
3869 if (Process
->VmTopDown
== 1)
3871 DPRINT1("VmTopDown not supported\n");
3872 Status
= STATUS_INVALID_PARAMETER
;
3873 goto FailPathNoLock
;
3877 // Check if the caller is reserving memory, or committing memory and letting
3878 // us pick the base address
3880 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3883 // Do not allow COPY_ON_WRITE through this API
3885 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3887 DPRINT1("Copy on write not allowed through this path\n");
3888 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3889 goto FailPathNoLock
;
3893 // Does the caller have an address in mind, or is this a blind commit?
3898 // This is a blind commit, all we need is the region size
3900 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3901 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3903 StartingAddress
= 0;
3908 // This is a reservation, so compute the starting address on the
3909 // expected 64KB granularity, and see where the ending address will
3910 // fall based on the aligned address and the passed in region size
3912 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3913 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3914 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3918 // Allocate and initialize the VAD
3920 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3921 ASSERT(Vad
!= NULL
);
3922 Vad
->u
.LongFlags
= 0;
3923 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3924 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3925 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3926 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3929 // Lock the address space and make sure the process isn't already dead
3931 AddressSpace
= MmGetCurrentAddressSpace();
3932 MmLockAddressSpace(AddressSpace
);
3933 if (Process
->VmDeleted
)
3935 Status
= STATUS_PROCESS_IS_TERMINATING
;
3940 // Did we have a base address? If no, find a valid address that is 64KB
3941 // aligned in the VAD tree. Otherwise, make sure that the address range
3942 // which was passed in isn't already conflicting with an existing address
3947 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3950 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3952 if (!NT_SUCCESS(Status
)) goto FailPath
;
3955 // Now we know where the allocation ends. Make sure it doesn't end up
3956 // somewhere in kernel mode.
3958 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3959 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3961 Status
= STATUS_NO_MEMORY
;
3965 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3966 EndingAddress
>> PAGE_SHIFT
,
3970 // The address specified is in conflict!
3972 Status
= STATUS_CONFLICTING_ADDRESSES
;
3977 // Write out the VAD fields for this allocation
3979 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3980 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3983 // FIXME: Should setup VAD bitmap
3985 Status
= STATUS_SUCCESS
;
3988 // Lock the working set and insert the VAD into the process VAD tree
3990 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
3991 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3992 MiInsertVad(Vad
, Process
);
3993 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
3996 // Update the virtual size of the process, and if this is now the highest
3997 // virtual size we have ever seen, update the peak virtual size to reflect
4000 Process
->VirtualSize
+= PRegionSize
;
4001 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4003 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4007 // Release address space and detach and dereference the target process if
4008 // it was different from the current process
4010 MmUnlockAddressSpace(AddressSpace
);
4011 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4012 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4015 // Use SEH to write back the base address and the region size. In the case
4016 // of an exception, we do not return back the exception code, as the memory
4017 // *has* been allocated. The caller would now have to call VirtualQuery
4018 // or do some other similar trick to actually find out where its memory
4019 // allocation ended up
4023 *URegionSize
= PRegionSize
;
4024 *UBaseAddress
= (PVOID
)StartingAddress
;
4026 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4030 return STATUS_SUCCESS
;
4034 // This is a MEM_COMMIT on top of an existing address which must have been
4035 // MEM_RESERVED already. Compute the start and ending base addresses based
4036 // on the user input, and then compute the actual region size once all the
4037 // alignments have been done.
4039 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4040 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4041 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4044 // Lock the address space and make sure the process isn't already dead
4046 AddressSpace
= MmGetCurrentAddressSpace();
4047 MmLockAddressSpace(AddressSpace
);
4048 if (Process
->VmDeleted
)
4050 DPRINT1("Process is dying\n");
4051 Status
= STATUS_PROCESS_IS_TERMINATING
;
4056 // Get the VAD for this address range, and make sure it exists
4058 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4059 EndingAddress
>> PAGE_SHIFT
,
4063 DPRINT1("Could not find a VAD for this allocation\n");
4064 Status
= STATUS_CONFLICTING_ADDRESSES
;
4069 // These kinds of VADs are illegal for this Windows function when trying to
4070 // commit an existing range
4072 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4073 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4074 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4076 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4077 Status
= STATUS_CONFLICTING_ADDRESSES
;
4082 // Make sure that this address range actually fits within the VAD for it
4084 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
4085 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4087 DPRINT1("Address range does not fit into the VAD\n");
4088 Status
= STATUS_CONFLICTING_ADDRESSES
;
4093 // Make sure this is an ARM3 section
4095 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4096 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4098 // Is this a previously reserved section being committed? If so, enter the
4099 // special section path
4101 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4104 // You cannot commit large page sections through this API
4106 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4108 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4109 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4114 // You can only use caching flags on a rotate VAD
4116 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4117 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4119 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4120 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4125 // We should make sure that the section's permissions aren't being
4128 if (FoundVad
->u
.VadFlags
.NoChange
)
4131 // Make sure it's okay to touch it
4133 Status
= MiCheckSecuredVad(FoundVad
,
4137 if (!NT_SUCCESS(Status
))
4139 DPRINT1("Secured VAD being messed around with\n");
4145 // ARM3 does not support file-backed sections, only shared memory
4147 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4150 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4152 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4153 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4155 DPRINT1("Invalid page protection for rotate VAD\n");
4156 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4161 // Compute PTE addresses and the quota charge, then grab the commit lock
4163 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4164 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4165 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4166 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4169 // Get the segment template PTE and start looping each page
4171 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4172 ASSERT(TempPte
.u
.Long
!= 0);
4173 while (PointerPte
<= LastPte
)
4176 // For each non-already-committed page, write the invalid template PTE
4178 if (PointerPte
->u
.Long
== 0)
4180 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4190 // Now do the commit accounting and release the lock
4192 ASSERT(QuotaCharge
>= QuotaFree
);
4193 QuotaCharge
-= QuotaFree
;
4194 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4195 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4198 // We are done with committing the section pages
4200 Status
= STATUS_SUCCESS
;
4205 // This is a specific ReactOS check because we only use normal VADs
4207 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4210 // While this is an actual Windows check
4212 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4215 // Throw out attempts to use copy-on-write through this API path
4217 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4219 DPRINT1("Write copy attempted when not allowed\n");
4220 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4225 // Initialize a demand-zero PTE
4228 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4231 // Get the PTE, PDE and the last PTE for this address range
4233 PointerPde
= MiAddressToPde(StartingAddress
);
4234 PointerPte
= MiAddressToPte(StartingAddress
);
4235 LastPte
= MiAddressToPte(EndingAddress
);
4238 // Update the commit charge in the VAD as well as in the process, and check
4239 // if this commit charge was now higher than the last recorded peak, in which
4240 // case we also update the peak
4242 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4243 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4244 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4246 Process
->CommitChargePeak
= Process
->CommitCharge
;
4250 // Lock the working set while we play with user pages and page tables
4252 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4255 // Make the current page table valid, and then loop each page within it
4257 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4258 while (PointerPte
<= LastPte
)
4261 // Have we crossed into a new page table?
4263 if (MiIsPteOnPdeBoundary(PointerPte
))
4266 // Get the PDE and now make it valid too
4268 PointerPde
= MiAddressToPte(PointerPte
);
4269 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4273 // Is this a zero PTE as expected?
4275 if (PointerPte
->u
.Long
== 0)
4278 // First increment the count of pages in the page table for this
4281 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4284 // And now write the invalid demand-zero PTE as requested
4286 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4288 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4291 // If the PTE was already decommitted, there is nothing else to do
4292 // but to write the new demand-zero PTE
4294 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4296 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4299 // We don't handle these scenarios yet
4301 if (PointerPte
->u
.Soft
.Valid
== 0)
4303 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4304 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4308 // There's a change in protection, remember this for later, but do
4309 // not yet handle it.
4311 ChangeProtection
= TRUE
;
4315 // Move to the next PTE
4321 // Release the working set lock, unlock the address space, and detach from
4322 // the target process if it was not the current process. Also dereference the
4323 // target process if this wasn't the case.
4325 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4326 Status
= STATUS_SUCCESS
;
4328 MmUnlockAddressSpace(AddressSpace
);
4331 // Check if we need to update the protection
4333 if (ChangeProtection
)
4335 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
4336 SIZE_T ProtectSize
= PRegionSize
;
4337 ULONG OldProtection
;
4340 // Change the protection of the region
4342 MiProtectVirtualMemory(Process
,
4343 &ProtectBaseAddress
,
4350 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4351 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4354 // Use SEH to write back the base address and the region size. In the case
4355 // of an exception, we strangely do return back the exception code, even
4356 // though the memory *has* been allocated. This mimics Windows behavior and
4357 // there is not much we can do about it.
4361 *URegionSize
= PRegionSize
;
4362 *UBaseAddress
= (PVOID
)StartingAddress
;
4364 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4366 Status
= _SEH2_GetExceptionCode();
4377 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4378 IN PVOID
* UBaseAddress
,
4379 IN PSIZE_T URegionSize
,
4382 PMEMORY_AREA MemoryArea
;
4385 ULONG_PTR CommitReduction
= 0;
4386 ULONG_PTR StartingAddress
, EndingAddress
;
4390 PMMSUPPORT AddressSpace
;
4391 PETHREAD CurrentThread
= PsGetCurrentThread();
4392 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4393 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4394 KAPC_STATE ApcState
;
4395 BOOLEAN Attached
= FALSE
;
4399 // Only two flags are supported
4401 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4403 DPRINT1("Invalid FreeType\n");
4404 return STATUS_INVALID_PARAMETER_4
;
4408 // Check if no flag was used, or if both flags were used
4410 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
4411 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
4413 DPRINT1("Invalid FreeType combination\n");
4414 return STATUS_INVALID_PARAMETER_4
;
4418 // Enter SEH for probe and capture. On failure, return back to the caller
4419 // with an exception violation.
4424 // Check for user-mode parameters and make sure that they are writeable
4426 if (PreviousMode
!= KernelMode
)
4428 ProbeForWritePointer(UBaseAddress
);
4429 ProbeForWriteUlong(URegionSize
);
4433 // Capture the current values
4435 PBaseAddress
= *UBaseAddress
;
4436 PRegionSize
= *URegionSize
;
4438 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4440 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4445 // Make sure the allocation isn't past the user area
4447 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4449 DPRINT1("Virtual free base above User Space\n");
4450 return STATUS_INVALID_PARAMETER_2
;
4454 // Make sure the allocation wouldn't overflow past the user area
4456 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4458 DPRINT1("Region size would overflow into kernel-memory\n");
4459 return STATUS_INVALID_PARAMETER_3
;
4463 // If this is for the current process, just use PsGetCurrentProcess
4465 if (ProcessHandle
== NtCurrentProcess())
4467 Process
= CurrentProcess
;
4472 // Otherwise, reference the process with VM rights and attach to it if
4473 // this isn't the current process. We must attach because we'll be touching
4474 // PTEs and PDEs that belong to user-mode memory, and also touching the
4475 // Working Set which is stored in Hyperspace.
4477 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4478 PROCESS_VM_OPERATION
,
4483 if (!NT_SUCCESS(Status
)) return Status
;
4484 if (CurrentProcess
!= Process
)
4486 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4492 // Lock the address space
4494 AddressSpace
= MmGetCurrentAddressSpace();
4495 MmLockAddressSpace(AddressSpace
);
4498 // If the address space is being deleted, fail the de-allocation since it's
4499 // too late to do anything about it
4501 if (Process
->VmDeleted
)
4503 DPRINT1("Process is dead\n");
4504 Status
= STATUS_PROCESS_IS_TERMINATING
;
4509 // Compute start and end addresses, and locate the VAD
4511 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4512 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4513 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4516 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4517 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4522 // If the range exceeds the VAD's ending VPN, fail this request
4524 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4526 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4527 Status
= STATUS_UNABLE_TO_FREE_VM
;
4532 // Only private memory (except rotate VADs) can be freed through here */
4534 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
4535 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
4536 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
4538 DPRINT1("Attempt to free section memory\n");
4539 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
4544 // ARM3 does not yet handle protected VM
4546 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4549 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4550 // and that is is an ARM3 memory area, and not a section view, as we currently
4551 // don't support freeing those though this interface.
4553 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4555 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4558 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4560 if (FreeType
& MEM_RELEASE
)
4563 // ARM3 only supports this VAD in this path
4565 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4568 // Is the caller trying to remove the whole VAD, or remove only a portion
4569 // of it? If no region size is specified, then the assumption is that the
4570 // whole VAD is to be destroyed
4575 // The caller must specify the base address identically to the range
4576 // that is stored in the VAD.
4578 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4580 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4581 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4586 // Now compute the actual start/end addresses based on the VAD
4588 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4589 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4592 // Finally lock the working set and remove the VAD from the VAD tree
4594 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4595 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4596 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4601 // This means the caller wants to release a specific region within
4602 // the range. We have to find out which range this is -- the following
4603 // possibilities exist plus their union (CASE D):
4605 // STARTING ADDRESS ENDING ADDRESS
4606 // [<========][========================================][=========>]
4607 // CASE A CASE B CASE C
4610 // First, check for case A or D
4612 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4617 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4620 // This is the easiest one to handle -- it is identical to
4621 // the code path above when the caller sets a zero region size
4622 // and the whole VAD is destroyed
4624 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4625 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4626 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4631 // This case is pretty easy too -- we compute a bunch of
4632 // pages to decommit, and then push the VAD's starting address
4633 // a bit further down, then decrement the commit charge
4635 // NOT YET IMPLEMENTED IN ARM3.
4637 DPRINT1("Case A not handled\n");
4638 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4642 // After analyzing the VAD, set it to NULL so that we don't
4643 // free it in the exit path
4651 // This is case B or case C. First check for case C
4653 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4655 PMEMORY_AREA MemoryArea
;
4658 // This is pretty easy and similar to case A. We compute the
4659 // amount of pages to decommit, update the VAD's commit charge
4660 // and then change the ending address of the VAD to be a bit
4663 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4664 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4668 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4669 // For ReactOS: shrink the corresponding memory area
4670 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4671 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
4672 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
4673 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4674 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
4679 // This is case B and the hardest one. Because we are removing
4680 // a chunk of memory from the very middle of the VAD, we must
4681 // actually split the VAD into two new VADs and compute the
4682 // commit charges for each of them, and reinsert new charges.
4684 // NOT YET IMPLEMENTED IN ARM3.
4686 DPRINT1("Case B not handled\n");
4687 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4692 // After analyzing the VAD, set it to NULL so that we don't
4693 // free it in the exit path
4700 // Now we have a range of pages to dereference, so call the right API
4701 // to do that and then release the working set, since we're done messing
4702 // around with process pages.
4704 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4705 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4706 Status
= STATUS_SUCCESS
;
4710 // Update the process counters
4712 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4713 Process
->CommitCharge
-= CommitReduction
;
4714 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4717 // Unlock the address space and free the VAD in failure cases. Next,
4718 // detach from the target process so we can write the region size and the
4719 // base address to the correct source process, and dereference the target
4722 MmUnlockAddressSpace(AddressSpace
);
4723 if (Vad
) ExFreePool(Vad
);
4724 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4725 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4728 // Use SEH to safely return the region size and the base address of the
4729 // deallocation. If we get an access violation, don't return a failure code
4730 // as the deallocation *has* happened. The caller will just have to figure
4731 // out another way to find out where it is (such as VirtualQuery).
4735 *URegionSize
= PRegionSize
;
4736 *UBaseAddress
= (PVOID
)StartingAddress
;
4738 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4746 // This is the decommit path. You cannot decommit from the following VADs in
4747 // Windows, so fail the vall
4749 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4750 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4751 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4753 DPRINT1("Trying to decommit from invalid VAD\n");
4754 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4759 // If the caller did not specify a region size, first make sure that this
4760 // region is actually committed. If it is, then compute the ending address
4761 // based on the VAD.
4765 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4767 DPRINT1("Decomitting non-committed memory\n");
4768 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4771 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4775 // Decommit the PTEs for the range plus the actual backing pages for the
4776 // range, then reduce that amount from the commit charge in the VAD
4778 CommitReduction
= MiAddressToPte(EndingAddress
) -
4779 MiAddressToPte(StartingAddress
) +
4781 MiDecommitPages((PVOID
)StartingAddress
,
4782 MiAddressToPte(EndingAddress
),
4785 ASSERT(CommitReduction
>= 0);
4786 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4787 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4790 // We are done, go to the exit path without freeing the VAD as it remains
4791 // valid since we have not released the allocation.
4794 Status
= STATUS_SUCCESS
;
4798 // In the failure path, we detach and derefernece the target process, and
4799 // return whatever failure code was sent.
4802 MmUnlockAddressSpace(AddressSpace
);
4803 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4804 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);