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
);
29 /* PRIVATE FUNCTIONS **********************************************************/
33 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
34 IN PEPROCESS CurrentProcess
)
37 BOOLEAN WsWasLocked
= FALSE
, LockChange
= FALSE
;
38 PETHREAD CurrentThread
= PsGetCurrentThread();
40 /* Must be a non-pool page table, since those are double-mapped already */
41 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
42 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
43 (PageTableVirtualAddress
> MmPagedPoolEnd
));
45 /* Working set lock or PFN lock should be held */
46 ASSERT(KeAreAllApcsDisabled() == TRUE
);
48 /* Check if the page table is valid */
49 while (!MmIsAddressValid(PageTableVirtualAddress
))
51 /* Check if the WS is locked */
52 if (CurrentThread
->OwnsProcessWorkingSetExclusive
)
54 /* Unlock the working set and remember it was locked */
55 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
60 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
61 if (!NT_SUCCESS(Status
))
63 /* This should not fail */
64 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
67 (ULONG_PTR
)CurrentProcess
,
68 (ULONG_PTR
)PageTableVirtualAddress
);
71 /* Lock the working set again */
72 if (WsWasLocked
) MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
74 /* This flag will be useful later when we do better locking */
78 /* Let caller know what the lock state is */
84 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
88 BOOLEAN LockChange
= FALSE
;
90 /* Must be e kernel address */
91 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
93 /* Check if the page is valid */
94 while (!MmIsAddressValid(VirtualAddress
))
96 /* Release the PFN database */
97 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
100 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
101 if (!NT_SUCCESS(Status
))
103 /* This should not fail */
104 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
108 (ULONG_PTR
)VirtualAddress
);
111 /* This flag will be useful later when we do better locking */
114 /* Lock the PFN database */
115 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
118 /* Let caller know what the lock state is */
124 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
125 IN PFN_NUMBER PageCount
,
127 OUT PPFN_NUMBER ValidPages
)
129 PFN_COUNT ActualPages
= 0;
130 PETHREAD CurrentThread
= PsGetCurrentThread();
132 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
134 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
136 /* Lock the system working set */
137 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
142 /* Make sure there's some data about the page */
143 if (PointerPte
->u
.Long
)
145 /* As always, only handle current ARM3 scenarios */
146 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
147 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
149 /* Normally this is one possibility -- freeing a valid page */
150 if (PointerPte
->u
.Hard
.Valid
)
152 /* Get the page PFN */
153 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
154 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
156 /* Should not have any working set data yet */
157 ASSERT(Pfn1
->u1
.WsIndex
== 0);
159 /* Actual valid, legitimate, pages */
160 if (ValidPages
) (*ValidPages
)++;
162 /* Get the page table entry */
163 PageTableIndex
= Pfn1
->u4
.PteFrame
;
164 Pfn2
= MiGetPfnEntry(PageTableIndex
);
166 /* Lock the PFN database */
167 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
169 /* Delete it the page */
170 MI_SET_PFN_DELETED(Pfn1
);
171 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
173 /* Decrement the page table too */
174 MiDecrementShareCount(Pfn2
, PageTableIndex
);
176 /* Release the PFN database */
177 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
179 /* Destroy the PTE */
180 PointerPte
->u
.Long
= 0;
183 /* Actual legitimate pages */
189 * The only other ARM3 possibility is a demand zero page, which would
190 * mean freeing some of the paged pool pages that haven't even been
191 * touched yet, as part of a larger allocation.
193 * Right now, we shouldn't expect any page file information in the PTE
195 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
197 /* Destroy the PTE */
198 PointerPte
->u
.Long
= 0;
206 /* Release the working set */
207 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
209 /* Flush the entire TLB */
210 KeFlushEntireTb(TRUE
, TRUE
);
218 MiDeletePte(IN PMMPTE PointerPte
,
219 IN PVOID VirtualAddress
,
220 IN PEPROCESS CurrentProcess
,
221 IN PMMPTE PrototypePte
)
225 PFN_NUMBER PageFrameIndex
;
228 /* PFN lock must be held */
229 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
231 /* Capture the PTE */
232 TempPte
= *PointerPte
;
234 /* We only support valid PTEs for now */
235 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
236 if (TempPte
.u
.Hard
.Valid
== 0)
238 /* Invalid PTEs not supported yet */
239 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
240 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
243 /* Get the PFN entry */
244 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
245 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
247 /* Check if this is a valid, prototype PTE */
248 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
250 /* Get the PDE and make sure it's faulted in */
251 PointerPde
= MiPteToPde(PointerPte
);
252 if (PointerPde
->u
.Hard
.Valid
== 0)
254 #if (_MI_PAGING_LEVELS == 2)
255 /* Could be paged pool access from a new process -- synchronize the page directories */
256 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
259 /* The PDE must be valid at this point */
260 KeBugCheckEx(MEMORY_MANAGEMENT
,
262 (ULONG_PTR
)PointerPte
,
264 (ULONG_PTR
)VirtualAddress
);
266 #if (_MI_PAGING_LEVELS == 2)
269 /* Drop the share count */
270 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
272 /* Either a fork, or this is the shared user data page */
273 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
275 /* If it's not the shared user page, then crash, since there's no fork() yet */
276 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
277 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
279 /* Must be some sort of memory corruption */
280 KeBugCheckEx(MEMORY_MANAGEMENT
,
282 (ULONG_PTR
)PointerPte
,
283 (ULONG_PTR
)PrototypePte
,
284 (ULONG_PTR
)Pfn1
->PteAddress
);
290 /* Make sure the saved PTE address is valid */
291 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
293 /* The PFN entry is illegal, or invalid */
294 KeBugCheckEx(MEMORY_MANAGEMENT
,
296 (ULONG_PTR
)PointerPte
,
298 (ULONG_PTR
)Pfn1
->PteAddress
);
301 /* There should only be 1 shared reference count */
302 ASSERT(Pfn1
->u2
.ShareCount
== 1);
304 /* Drop the reference on the page table. */
305 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
307 /* Mark the PFN for deletion and dereference what should be the last ref */
308 MI_SET_PFN_DELETED(Pfn1
);
309 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
311 /* We should eventually do this */
312 //CurrentProcess->NumberOfPrivatePages--;
315 /* Destroy the PTE and flush the TLB */
316 PointerPte
->u
.Long
= 0;
322 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
323 IN ULONG_PTR EndingAddress
,
326 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
329 PEPROCESS CurrentProcess
;
331 BOOLEAN AddressGap
= FALSE
;
332 PSUBSECTION Subsection
;
333 PUSHORT UsedPageTableEntries
;
335 /* Get out if this is a fake VAD, RosMm will free the marea pages */
336 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
338 /* Grab the process and PTE/PDE for the address being deleted */
339 CurrentProcess
= PsGetCurrentProcess();
340 PointerPde
= MiAddressToPde(Va
);
341 PointerPte
= MiAddressToPte(Va
);
343 /* Check if this is a section VAD or a VM VAD */
344 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
346 /* Don't worry about prototypes */
347 PrototypePte
= LastPrototypePte
= NULL
;
351 /* Get the prototype PTE */
352 PrototypePte
= Vad
->FirstPrototypePte
;
353 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
356 /* In all cases, we don't support fork() yet */
357 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
359 /* Loop the PTE for each VA */
362 /* First keep going until we find a valid PDE */
363 while (!PointerPde
->u
.Long
)
365 /* There are gaps in the address space */
368 /* Still no valid PDE, try the next 4MB (or whatever) */
371 /* Update the PTE on this new boundary */
372 PointerPte
= MiPteToAddress(PointerPde
);
374 /* Check if all the PDEs are invalid, so there's nothing to free */
375 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
376 if (Va
> EndingAddress
) return;
379 /* Now check if the PDE is mapped in */
380 if (!PointerPde
->u
.Hard
.Valid
)
382 /* It isn't, so map it in */
383 PointerPte
= MiPteToAddress(PointerPde
);
384 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
387 /* Now we should have a valid PDE, mapped in, and still have some VA */
388 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
389 ASSERT(Va
<= EndingAddress
);
390 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
392 /* Check if this is a section VAD with gaps in it */
393 if ((AddressGap
) && (LastPrototypePte
))
395 /* We need to skip to the next correct prototype PTE */
396 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
398 /* And we need the subsection to skip to the next last prototype PTE */
399 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
403 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
407 /* No more subsections, we are done with prototype PTEs */
412 /* Lock the PFN Database while we delete the PTEs */
413 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
416 /* Capture the PDE and make sure it exists */
417 TempPte
= *PointerPte
;
420 DPRINT("Decrement used PTEs by address: %lx\n", Va
);
421 (*UsedPageTableEntries
)--;
422 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
423 DPRINT("Refs: %lx\n", (*UsedPageTableEntries
));
425 /* Check if the PTE is actually mapped in */
426 if (TempPte
.u
.Long
& 0xFFFFFC01)
428 /* Are we dealing with section VAD? */
429 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
431 /* We need to skip to the next correct prototype PTE */
432 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
434 /* And we need the subsection to skip to the next last prototype PTE */
435 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
439 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
443 /* No more subsections, we are done with prototype PTEs */
448 /* Check for prototype PTE */
449 if ((TempPte
.u
.Hard
.Valid
== 0) &&
450 (TempPte
.u
.Soft
.Prototype
== 1))
453 PointerPte
->u
.Long
= 0;
457 /* Delete the PTE proper */
458 MiDeletePte(PointerPte
,
466 /* The PTE was never mapped, just nuke it here */
467 PointerPte
->u
.Long
= 0;
471 /* Update the address and PTE for it */
476 /* Making sure the PDE is still valid */
477 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
479 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
481 /* The PDE should still be valid at this point */
482 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
484 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va
, PointerPde
->u
.Hard
.PageFrameNumber
);
485 if (!(*UsedPageTableEntries
))
487 DPRINT("They are!\n");
488 if (PointerPde
->u
.Long
!= 0)
490 DPRINT("PDE active: %lx in %16s\n", PointerPde
->u
.Hard
.PageFrameNumber
, CurrentProcess
->ImageFileName
);
492 /* Delete the PTE proper */
493 MiDeletePte(PointerPde
,
494 MiPteToAddress(PointerPde
),
500 /* Release the lock and get out if we're done */
501 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
502 if (Va
> EndingAddress
) return;
504 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
505 PointerPde
= MiAddressToPde(Va
);
511 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
512 OUT PBOOLEAN HaveBadAddress
,
513 OUT PULONG_PTR BadAddress
)
515 PEXCEPTION_RECORD ExceptionRecord
;
521 *HaveBadAddress
= FALSE
;
524 // Get the exception record
526 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
529 // Look at the exception code
531 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
532 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
533 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
536 // We can tell the address if we have more than one parameter
538 if (ExceptionRecord
->NumberParameters
> 1)
541 // Return the address
543 *HaveBadAddress
= TRUE
;
544 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
549 // Continue executing the next handler
551 return EXCEPTION_EXECUTE_HANDLER
;
556 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
557 IN PVOID SourceAddress
,
558 IN PEPROCESS TargetProcess
,
559 OUT PVOID TargetAddress
,
560 IN SIZE_T BufferSize
,
561 IN KPROCESSOR_MODE PreviousMode
,
562 OUT PSIZE_T ReturnSize
)
564 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
565 PMDL Mdl
= (PMDL
)MdlBuffer
;
566 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
567 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
568 volatile BOOLEAN PagesLocked
;
569 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
570 volatile PVOID MdlAddress
;
572 BOOLEAN HaveBadAddress
;
573 ULONG_PTR BadAddress
;
574 NTSTATUS Status
= STATUS_SUCCESS
;
578 // Calculate the maximum amount of data to move
580 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
581 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
582 CurrentSize
= TotalSize
;
583 RemainingSize
= BufferSize
;
586 // Loop as long as there is still data
588 while (RemainingSize
> 0)
591 // Check if this transfer will finish everything off
593 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
596 // Attach to the source address space
598 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
601 // Reset state for this pass
605 FailedInMoving
= FALSE
;
606 ASSERT(FailedInProbe
== FALSE
);
609 // Protect user-mode copy
614 // If this is our first time, probe the buffer
616 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
619 // Catch a failure here
621 FailedInProbe
= TRUE
;
626 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
631 FailedInProbe
= FALSE
;
635 // Initialize and probe and lock the MDL
637 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
638 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
644 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
653 // Use our SEH handler to pick this up
655 FailedInMapping
= TRUE
;
656 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
660 // Now let go of the source and grab to the target process
662 KeUnstackDetachProcess(&ApcState
);
663 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
666 // Check if this is our first time through
668 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
671 // Catch a failure here
673 FailedInProbe
= TRUE
;
678 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
683 FailedInProbe
= FALSE
;
687 // Now do the actual move
689 FailedInMoving
= TRUE
;
690 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
692 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
697 // Detach from whoever we may be attached to
699 KeUnstackDetachProcess(&ApcState
);
702 // Check if we had mapped the pages
704 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
707 // Check if we had locked the pages
709 if (PagesLocked
) MmUnlockPages(Mdl
);
712 // Check if we hit working set quota
714 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
719 return STATUS_WORKING_SET_QUOTA
;
723 // Check if we failed during the probe or mapping
725 if ((FailedInProbe
) || (FailedInMapping
))
730 Status
= _SEH2_GetExceptionCode();
731 _SEH2_YIELD(return Status
);
735 // Otherwise, we failed probably during the move
737 *ReturnSize
= BufferSize
- RemainingSize
;
741 // Check if we know exactly where we stopped copying
746 // Return the exact number of bytes copied
748 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
753 // Return partial copy
755 Status
= STATUS_PARTIAL_COPY
;
760 // Check for SEH status
762 if (Status
!= STATUS_SUCCESS
) return Status
;
765 // Detach from target
767 KeUnstackDetachProcess(&ApcState
);
772 MmUnmapLockedPages(MdlAddress
, Mdl
);
776 // Update location and size
778 RemainingSize
-= CurrentSize
;
779 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
780 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
786 *ReturnSize
= BufferSize
;
787 return STATUS_SUCCESS
;
792 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
793 IN PVOID SourceAddress
,
794 IN PEPROCESS TargetProcess
,
795 OUT PVOID TargetAddress
,
796 IN SIZE_T BufferSize
,
797 IN KPROCESSOR_MODE PreviousMode
,
798 OUT PSIZE_T ReturnSize
)
800 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
801 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
802 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
803 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
806 BOOLEAN HaveBadAddress
;
807 ULONG_PTR BadAddress
;
808 NTSTATUS Status
= STATUS_SUCCESS
;
812 // Calculate the maximum amount of data to move
814 TotalSize
= MI_MAX_TRANSFER_SIZE
;
815 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
816 CurrentSize
= TotalSize
;
817 RemainingSize
= BufferSize
;
820 // Check if we can use the stack
822 if (BufferSize
<= MI_POOL_COPY_BYTES
)
827 PoolAddress
= (PVOID
)StackBuffer
;
834 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
835 if (!PoolAddress
) ASSERT(FALSE
);
836 HavePoolAddress
= TRUE
;
840 // Loop as long as there is still data
842 while (RemainingSize
> 0)
845 // Check if this transfer will finish everything off
847 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
850 // Attach to the source address space
852 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
855 // Reset state for this pass
857 FailedInMoving
= FALSE
;
858 ASSERT(FailedInProbe
== FALSE
);
861 // Protect user-mode copy
866 // If this is our first time, probe the buffer
868 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
871 // Catch a failure here
873 FailedInProbe
= TRUE
;
878 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
883 FailedInProbe
= FALSE
;
889 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
892 // Now let go of the source and grab to the target process
894 KeUnstackDetachProcess(&ApcState
);
895 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
898 // Check if this is our first time through
900 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
903 // Catch a failure here
905 FailedInProbe
= TRUE
;
910 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
915 FailedInProbe
= FALSE
;
919 // Now do the actual move
921 FailedInMoving
= TRUE
;
922 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
924 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
929 // Detach from whoever we may be attached to
931 KeUnstackDetachProcess(&ApcState
);
934 // Check if we had allocated pool
936 if (HavePoolAddress
) ExFreePool(PoolAddress
);
939 // Check if we failed during the probe
946 Status
= _SEH2_GetExceptionCode();
947 _SEH2_YIELD(return Status
);
951 // Otherwise, we failed, probably during the move
953 *ReturnSize
= BufferSize
- RemainingSize
;
957 // Check if we know exactly where we stopped copying
962 // Return the exact number of bytes copied
964 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
969 // Return partial copy
971 Status
= STATUS_PARTIAL_COPY
;
976 // Check for SEH status
978 if (Status
!= STATUS_SUCCESS
) return Status
;
981 // Detach from target
983 KeUnstackDetachProcess(&ApcState
);
986 // Update location and size
988 RemainingSize
-= CurrentSize
;
989 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
990 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
995 // Check if we had allocated pool
997 if (HavePoolAddress
) ExFreePool(PoolAddress
);
1002 *ReturnSize
= BufferSize
;
1003 return STATUS_SUCCESS
;
1008 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1009 IN PVOID SourceAddress
,
1010 IN PEPROCESS TargetProcess
,
1011 OUT PVOID TargetAddress
,
1012 IN SIZE_T BufferSize
,
1013 IN KPROCESSOR_MODE PreviousMode
,
1014 OUT PSIZE_T ReturnSize
)
1017 PEPROCESS Process
= SourceProcess
;
1020 // Don't accept zero-sized buffers
1022 if (!BufferSize
) return STATUS_SUCCESS
;
1025 // If we are copying from ourselves, lock the target instead
1027 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1030 // Acquire rundown protection
1032 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1037 return STATUS_PROCESS_IS_TERMINATING
;
1041 // See if we should use the pool copy
1043 if (BufferSize
> MI_POOL_COPY_BYTES
)
1048 Status
= MiDoMappedCopy(SourceProcess
,
1061 Status
= MiDoPoolCopy(SourceProcess
,
1073 ExReleaseRundownProtection(&Process
->RundownProtect
);
1079 MmFlushVirtualMemory(IN PEPROCESS Process
,
1080 IN OUT PVOID
*BaseAddress
,
1081 IN OUT PSIZE_T RegionSize
,
1082 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1090 return STATUS_SUCCESS
;
1095 MiGetPageProtection(IN PMMPTE PointerPte
)
1101 /* Copy this PTE's contents */
1102 TempPte
= *PointerPte
;
1104 /* Assure it's not totally zero */
1105 ASSERT(TempPte
.u
.Long
);
1107 /* Check for a special prototype format */
1108 if (TempPte
.u
.Soft
.Valid
== 0 &&
1109 TempPte
.u
.Soft
.Prototype
== 1)
1111 /* Unsupported now */
1116 /* In the easy case of transition or demand zero PTE just return its protection */
1117 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1119 /* If we get here, the PTE is valid, so look up the page in PFN database */
1120 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1122 if (!Pfn
->u3
.e1
.PrototypePte
)
1124 /* Return protection of the original pte */
1125 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1128 /* This is hardware PTE */
1132 return PAGE_NOACCESS
;
1137 MiQueryAddressState(IN PVOID Va
,
1139 IN PEPROCESS TargetProcess
,
1140 OUT PULONG ReturnedProtect
,
1147 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1148 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1149 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1150 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1152 /* Only normal VADs supported */
1153 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1155 /* Get the PDE and PTE for the address */
1156 PointerPde
= MiAddressToPde(Va
);
1157 PointerPte
= MiAddressToPte(Va
);
1159 /* Return the next range */
1160 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1162 /* Loop to make sure the PDE is valid */
1168 /* Is the PDE empty? */
1169 if (!PointerPde
->u
.Long
)
1171 /* No address in this range used yet, move to the next PDE range */
1172 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1176 /* The PDE is not empty, but is it faulted in? */
1177 if (!PointerPde
->u
.Hard
.Valid
)
1179 /* It isn't, go ahead and do the fault */
1180 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1184 /* Check if the PDE was faulted in, making the PTE readable */
1185 if (!LockChange
) ValidPte
= TRUE
;
1186 } while (LockChange
);
1188 /* Is it safe to try reading the PTE? */
1191 /* FIXME: watch out for large pages */
1193 /* Capture the PTE */
1194 TempPte
= *PointerPte
;
1197 /* The PTE is valid, so it's not zeroed out */
1198 DemandZeroPte
= FALSE
;
1200 /* Check if it's valid or has a valid protection mask */
1201 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1202 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1203 (TempPte
.u
.Hard
.Valid
== 1))
1205 /* This means it's committed */
1208 /* Get protection state of this page */
1209 Protect
= MiGetPageProtection(PointerPte
);
1213 /* Otherwise our defaults should hold */
1214 ASSERT(Protect
== 0);
1215 ASSERT(State
== MEM_RESERVE
);
1220 /* Check if this was a demand-zero PTE, since we need to find the state */
1223 /* Check if the VAD is for committed memory */
1224 if (Vad
->u
.VadFlags
.MemCommit
)
1226 /* This is committed memory */
1229 /* Convert the protection */
1230 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1234 /* Return the protection code */
1235 *ReturnedProtect
= Protect
;
1241 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1242 IN PVOID BaseAddress
,
1243 OUT PVOID MemoryInformation
,
1244 IN SIZE_T MemoryInformationLength
,
1245 OUT PSIZE_T ReturnLength
)
1247 PEPROCESS TargetProcess
;
1248 NTSTATUS Status
= STATUS_SUCCESS
;
1250 PVOID Address
, NextAddress
;
1251 BOOLEAN Found
= FALSE
;
1252 ULONG NewProtect
, NewState
;
1254 MEMORY_BASIC_INFORMATION MemoryInfo
;
1255 KAPC_STATE ApcState
;
1256 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1257 PMEMORY_AREA MemoryArea
;
1258 SIZE_T ResultLength
;
1260 /* Check for illegal addresses in user-space, or the shared memory area */
1261 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1262 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1264 Address
= PAGE_ALIGN(BaseAddress
);
1266 /* Make up an info structure describing this range */
1267 MemoryInfo
.BaseAddress
= Address
;
1268 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1269 MemoryInfo
.Type
= MEM_PRIVATE
;
1271 /* Special case for shared data */
1272 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1274 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1275 MemoryInfo
.State
= MEM_COMMIT
;
1276 MemoryInfo
.Protect
= PAGE_READONLY
;
1277 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1281 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1282 MemoryInfo
.State
= MEM_RESERVE
;
1283 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1284 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1287 /* Return the data, NtQueryInformation already probed it*/
1288 if (PreviousMode
!= KernelMode
)
1292 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1293 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1295 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1297 Status
= _SEH2_GetExceptionCode();
1303 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1304 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1310 /* Check if this is for a local or remote process */
1311 if (ProcessHandle
== NtCurrentProcess())
1313 TargetProcess
= PsGetCurrentProcess();
1317 /* Reference the target process */
1318 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1319 PROCESS_QUERY_INFORMATION
,
1321 ExGetPreviousMode(),
1322 (PVOID
*)&TargetProcess
,
1324 if (!NT_SUCCESS(Status
)) return Status
;
1326 /* Attach to it now */
1327 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1331 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1332 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1334 /* Scan on the right */
1335 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1336 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1339 /* Check if this VAD covers the allocation range */
1340 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1341 (BaseVpn
<= Vad
->EndingVpn
))
1348 /* Check if this VAD is too high */
1349 if (BaseVpn
< Vad
->StartingVpn
)
1351 /* Stop if there is no left child */
1352 if (!Vad
->LeftChild
) break;
1354 /* Search on the left next */
1355 Vad
= Vad
->LeftChild
;
1359 /* Then this VAD is too low, keep searching on the right */
1360 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1362 /* Stop if there is no right child */
1363 if (!Vad
->RightChild
) break;
1365 /* Search on the right next */
1366 Vad
= Vad
->RightChild
;
1371 /* Was a VAD found? */
1374 Address
= PAGE_ALIGN(BaseAddress
);
1376 /* Calculate region size */
1379 if (Vad
->StartingVpn
>= BaseVpn
)
1381 /* Region size is the free space till the start of that VAD */
1382 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1386 /* Get the next VAD */
1387 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1390 /* Region size is the free space till the start of that VAD */
1391 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1395 /* Maximum possible region size with that base address */
1396 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1402 /* Maximum possible region size with that base address */
1403 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1406 /* Check if we were attached */
1407 if (ProcessHandle
!= NtCurrentProcess())
1409 /* Detach and derefernece the process */
1410 KeUnstackDetachProcess(&ApcState
);
1411 ObDereferenceObject(TargetProcess
);
1414 /* Build the rest of the initial information block */
1415 MemoryInfo
.BaseAddress
= Address
;
1416 MemoryInfo
.AllocationBase
= NULL
;
1417 MemoryInfo
.AllocationProtect
= 0;
1418 MemoryInfo
.State
= MEM_FREE
;
1419 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1420 MemoryInfo
.Type
= 0;
1422 /* Return the data, NtQueryInformation already probed it*/
1423 if (PreviousMode
!= KernelMode
)
1427 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1428 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1432 Status
= _SEH2_GetExceptionCode();
1438 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1439 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1445 /* Set the correct memory type based on what kind of VAD this is */
1446 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1447 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1449 MemoryInfo
.Type
= MEM_PRIVATE
;
1451 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1453 MemoryInfo
.Type
= MEM_IMAGE
;
1457 MemoryInfo
.Type
= MEM_MAPPED
;
1460 /* Lock the address space of the process */
1461 MmLockAddressSpace(&TargetProcess
->Vm
);
1463 /* Find the memory area the specified address belongs to */
1464 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1465 ASSERT(MemoryArea
!= NULL
);
1467 /* Determine information dependent on the memory area type */
1468 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1470 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1471 ASSERT(NT_SUCCESS(Status
));
1475 /* Build the initial information block */
1476 Address
= PAGE_ALIGN(BaseAddress
);
1477 MemoryInfo
.BaseAddress
= Address
;
1478 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1479 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1480 MemoryInfo
.Type
= MEM_PRIVATE
;
1482 /* Find the largest chunk of memory which has the same state and protection mask */
1483 MemoryInfo
.State
= MiQueryAddressState(Address
,
1486 &MemoryInfo
.Protect
,
1488 Address
= NextAddress
;
1489 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1491 /* Keep going unless the state or protection mask changed */
1492 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1493 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1494 Address
= NextAddress
;
1497 /* Now that we know the last VA address, calculate the region size */
1498 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1501 /* Unlock the address space of the process */
1502 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1504 /* Check if we were attached */
1505 if (ProcessHandle
!= NtCurrentProcess())
1507 /* Detach and derefernece the process */
1508 KeUnstackDetachProcess(&ApcState
);
1509 ObDereferenceObject(TargetProcess
);
1512 /* Return the data, NtQueryInformation already probed it*/
1513 if (PreviousMode
!= KernelMode
)
1517 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1518 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1520 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1522 Status
= _SEH2_GetExceptionCode();
1528 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1529 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1533 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1534 "State: %lx Type: %lx Size: %lx\n",
1535 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1536 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1537 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1544 MiProtectVirtualMemory(IN PEPROCESS Process
,
1545 IN OUT PVOID
*BaseAddress
,
1546 IN OUT PSIZE_T NumberOfBytesToProtect
,
1547 IN ULONG NewAccessProtection
,
1548 OUT PULONG OldAccessProtection OPTIONAL
)
1550 PMEMORY_AREA MemoryArea
;
1552 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1553 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1555 return MiRosProtectVirtualMemory(Process
,
1557 NumberOfBytesToProtect
,
1558 NewAccessProtection
,
1559 OldAccessProtection
);
1563 return STATUS_CONFLICTING_ADDRESSES
;
1568 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
1569 IN PEPROCESS TargetProcess
,
1572 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
1575 // Sanity checks. The latter is because we only use this function with the
1576 // PFN lock not held, so it may go away in the future.
1578 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1579 ASSERT(OldIrql
== MM_NOIRQL
);
1582 // Also get the PPE and PXE. This is okay not to #ifdef because they will
1583 // return the same address as the PDE on 2-level page table systems.
1585 // If everything is already valid, there is nothing to do.
1587 PointerPpe
= MiAddressToPte(PointerPde
);
1588 PointerPxe
= MiAddressToPde(PointerPde
);
1589 if ((PointerPxe
->u
.Hard
.Valid
) &&
1590 (PointerPpe
->u
.Hard
.Valid
) &&
1591 (PointerPde
->u
.Hard
.Valid
))
1597 // At least something is invalid, so begin by getting the PTE for the PDE itself
1598 // and then lookup each additional level. We must do it in this precise order
1599 // because the pagfault.c code (as well as in Windows) depends that the next
1600 // level up (higher) must be valid when faulting a lower level
1602 PointerPte
= MiPteToAddress(PointerPde
);
1606 // Make sure APCs continued to be disabled
1608 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1611 // First, make the PXE valid if needed
1613 if (!PointerPxe
->u
.Hard
.Valid
)
1615 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1616 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1622 if (!PointerPpe
->u
.Hard
.Valid
)
1624 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1625 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1629 // And finally, make the PDE itself valid.
1631 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1634 // This should've worked the first time so the loop is really just for
1635 // show -- ASSERT that we're actually NOT going to be looping.
1637 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1638 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1639 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1640 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
1641 !(PointerPpe
->u
.Hard
.Valid
) ||
1642 !(PointerPde
->u
.Hard
.Valid
));
1647 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
1653 PFN_NUMBER PageFrameIndex
;
1657 // Acquire the PFN lock and loop all the PTEs in the list
1659 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1660 for (i
= 0; i
!= Count
; i
++)
1663 // The PTE must currently be valid
1665 TempPte
= *ValidPteList
[i
];
1666 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1669 // Get the PFN entry for the page itself, and then for its page table
1671 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1672 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1673 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1676 // Decrement the share count on the page table, and then on the page
1679 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1680 MI_SET_PFN_DELETED(Pfn1
);
1681 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1684 // Make the page decommitted
1686 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
1690 // All the PTEs have been dereferenced and made invalid, flush the TLB now
1691 // and then release the PFN lock
1694 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1699 MiDecommitPages(IN PVOID StartingAddress
,
1700 IN PMMPTE EndingPte
,
1701 IN PEPROCESS Process
,
1704 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
1705 ULONG CommitReduction
= 0;
1706 PMMPTE ValidPteList
[256];
1710 PUSHORT UsedPageTableEntries
;
1711 PETHREAD CurrentThread
= PsGetCurrentThread();
1714 // Get the PTE and PTE for the address, and lock the working set
1715 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
1716 // commited range ends so that we can do the right accounting.
1718 PointerPde
= MiAddressToPde(StartingAddress
);
1719 PointerPte
= MiAddressToPte(StartingAddress
);
1720 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
1721 MiLockWorkingSet(CurrentThread
, &Process
->Vm
);
1724 // Make the PDE valid, and now loop through each page's worth of data
1726 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1727 while (PointerPte
<= EndingPte
)
1730 // Check if we've crossed a PDE boundary
1732 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
1735 // Get the new PDE and flush the valid PTEs we had built up until
1736 // now. This helps reduce the amount of TLB flushing we have to do.
1737 // Note that Windows does a much better job using timestamps and
1738 // such, and does not flush the entire TLB all the time, but right
1739 // now we have bigger problems to worry about than TLB flushing.
1741 PointerPde
= MiAddressToPde(StartingAddress
);
1744 MiProcessValidPteList(ValidPteList
, PteCount
);
1749 // Make this PDE valid
1751 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1755 // Read this PTE. It might be active or still demand-zero.
1757 PteContents
= *PointerPte
;
1758 if (PteContents
.u
.Long
)
1761 // The PTE is active. It might be valid and in a working set, or
1762 // it might be a prototype PTE or paged out or even in transition.
1764 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
1767 // It's already decommited, so there's nothing for us to do here
1774 // Remove it from the counters, and check if it was valid or not
1776 //Process->NumberOfPrivatePages--;
1777 if (PteContents
.u
.Hard
.Valid
)
1780 // It's valid. At this point make sure that it is not a ROS
1781 // PFN. Also, we don't support ProtoPTEs in this code path.
1783 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
1784 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
1785 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
1788 // Flush any pending PTEs that we had not yet flushed, if our
1789 // list has gotten too big, then add this PTE to the flush list.
1791 if (PteCount
== 256)
1793 MiProcessValidPteList(ValidPteList
, PteCount
);
1796 ValidPteList
[PteCount
++] = PointerPte
;
1801 // We do not support any of these other scenarios at the moment
1803 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
1804 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
1805 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
1808 // So the only other possibility is that it is still a demand
1809 // zero PTE, in which case we undo the accounting we did
1810 // earlier and simply make the page decommitted.
1812 //Process->NumberOfPrivatePages++;
1813 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
1820 // This used to be a zero PTE and it no longer is, so we must add a
1821 // reference to the pagetable.
1823 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(StartingAddress
)];
1824 (*UsedPageTableEntries
)++;
1825 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
1828 // Next, we account for decommitted PTEs and make the PTE as such
1830 if (PointerPte
> CommitPte
) CommitReduction
++;
1831 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
1835 // Move to the next PTE and the next address
1838 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
1842 // Flush any dangling PTEs from the loop in the last page table, and then
1843 // release the working set and return the commit reduction accounting.
1845 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
1846 MiUnlockWorkingSet(CurrentThread
, &Process
->Vm
);
1847 return CommitReduction
;
1850 /* PUBLIC FUNCTIONS ***********************************************************/
1857 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1868 MmSecureVirtualMemory(IN PVOID Address
,
1872 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1881 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1883 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1886 /* SYSTEM CALLS ***************************************************************/
1890 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1891 IN PVOID BaseAddress
,
1893 IN SIZE_T NumberOfBytesToRead
,
1894 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1896 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1898 NTSTATUS Status
= STATUS_SUCCESS
;
1899 SIZE_T BytesRead
= 0;
1903 // Check if we came from user mode
1905 if (PreviousMode
!= KernelMode
)
1908 // Validate the read addresses
1910 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1911 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1912 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1913 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1916 // Don't allow to write into kernel space
1918 return STATUS_ACCESS_VIOLATION
;
1922 // Enter SEH for probe
1927 // Probe the output value
1929 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1931 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1934 // Get exception code
1936 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1942 // Don't do zero-byte transfers
1944 if (NumberOfBytesToRead
)
1947 // Reference the process
1949 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1955 if (NT_SUCCESS(Status
))
1960 Status
= MmCopyVirtualMemory(Process
,
1962 PsGetCurrentProcess(),
1964 NumberOfBytesToRead
,
1969 // Dereference the process
1971 ObDereferenceObject(Process
);
1976 // Check if the caller sent this parameter
1978 if (NumberOfBytesRead
)
1981 // Enter SEH to guard write
1986 // Return the number of bytes read
1988 *NumberOfBytesRead
= BytesRead
;
1990 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2004 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2005 IN PVOID BaseAddress
,
2007 IN SIZE_T NumberOfBytesToWrite
,
2008 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2010 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2012 NTSTATUS Status
= STATUS_SUCCESS
;
2013 SIZE_T BytesWritten
= 0;
2017 // Check if we came from user mode
2019 if (PreviousMode
!= KernelMode
)
2022 // Validate the read addresses
2024 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2025 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2026 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2027 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2030 // Don't allow to write into kernel space
2032 return STATUS_ACCESS_VIOLATION
;
2036 // Enter SEH for probe
2041 // Probe the output value
2043 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2045 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2048 // Get exception code
2050 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2056 // Don't do zero-byte transfers
2058 if (NumberOfBytesToWrite
)
2061 // Reference the process
2063 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2069 if (NT_SUCCESS(Status
))
2074 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2078 NumberOfBytesToWrite
,
2083 // Dereference the process
2085 ObDereferenceObject(Process
);
2090 // Check if the caller sent this parameter
2092 if (NumberOfBytesWritten
)
2095 // Enter SEH to guard write
2100 // Return the number of bytes written
2102 *NumberOfBytesWritten
= BytesWritten
;
2104 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2118 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2119 IN OUT PVOID
*UnsafeBaseAddress
,
2120 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2121 IN ULONG NewAccessProtection
,
2122 OUT PULONG UnsafeOldAccessProtection
)
2125 ULONG OldAccessProtection
;
2127 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2128 PVOID BaseAddress
= NULL
;
2129 SIZE_T NumberOfBytesToProtect
= 0;
2130 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2132 BOOLEAN Attached
= FALSE
;
2133 KAPC_STATE ApcState
;
2137 // Check for valid protection flags
2139 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2140 if (Protection
!= PAGE_NOACCESS
&&
2141 Protection
!= PAGE_READONLY
&&
2142 Protection
!= PAGE_READWRITE
&&
2143 Protection
!= PAGE_WRITECOPY
&&
2144 Protection
!= PAGE_EXECUTE
&&
2145 Protection
!= PAGE_EXECUTE_READ
&&
2146 Protection
!= PAGE_EXECUTE_READWRITE
&&
2147 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2152 return STATUS_INVALID_PAGE_PROTECTION
;
2156 // Check if we came from user mode
2158 if (PreviousMode
!= KernelMode
)
2161 // Enter SEH for probing
2166 // Validate all outputs
2168 ProbeForWritePointer(UnsafeBaseAddress
);
2169 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2170 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2175 BaseAddress
= *UnsafeBaseAddress
;
2176 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2181 // Get exception code
2183 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2192 BaseAddress
= *UnsafeBaseAddress
;
2193 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2197 // Catch illegal base address
2199 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2202 // Catch illegal region size
2204 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2209 return STATUS_INVALID_PARAMETER_3
;
2213 // 0 is also illegal
2215 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2218 // Get a reference to the process
2220 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2221 PROCESS_VM_OPERATION
,
2226 if (!NT_SUCCESS(Status
)) return Status
;
2229 // Check if we should attach
2231 if (CurrentProcess
!= Process
)
2236 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2241 // Do the actual work
2243 Status
= MiProtectVirtualMemory(Process
,
2245 &NumberOfBytesToProtect
,
2246 NewAccessProtection
,
2247 &OldAccessProtection
);
2252 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2255 // Release reference
2257 ObDereferenceObject(Process
);
2260 // Enter SEH to return data
2265 // Return data to user
2267 *UnsafeOldAccessProtection
= OldAccessProtection
;
2268 *UnsafeBaseAddress
= BaseAddress
;
2269 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2271 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2284 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2285 IN OUT PVOID
*BaseAddress
,
2286 IN OUT PSIZE_T NumberOfBytesToLock
,
2290 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2292 BOOLEAN Attached
= FALSE
;
2293 KAPC_STATE ApcState
;
2294 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2295 PVOID CapturedBaseAddress
;
2296 SIZE_T CapturedBytesToLock
;
2302 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2305 // Invalid set of flags
2307 return STATUS_INVALID_PARAMETER
;
2311 // At least one flag must be specified
2313 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2318 return STATUS_INVALID_PARAMETER
;
2322 // Enter SEH for probing
2327 // Validate output data
2329 ProbeForWritePointer(BaseAddress
);
2330 ProbeForWriteSize_t(NumberOfBytesToLock
);
2335 CapturedBaseAddress
= *BaseAddress
;
2336 CapturedBytesToLock
= *NumberOfBytesToLock
;
2338 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2341 // Get exception code
2343 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2348 // Catch illegal base address
2350 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2353 // Catch illegal region size
2355 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2360 return STATUS_INVALID_PARAMETER
;
2364 // 0 is also illegal
2366 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2369 // Get a reference to the process
2371 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2372 PROCESS_VM_OPERATION
,
2377 if (!NT_SUCCESS(Status
)) return Status
;
2380 // Check if this is is system-mapped
2382 if (MapType
& MAP_SYSTEM
)
2385 // Check for required privilege
2387 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2390 // Fail: Don't have it
2392 ObDereferenceObject(Process
);
2393 return STATUS_PRIVILEGE_NOT_HELD
;
2398 // Check if we should attach
2400 if (CurrentProcess
!= Process
)
2405 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2417 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2420 // Release reference
2422 ObDereferenceObject(Process
);
2425 // Enter SEH to return data
2430 // Return data to user
2432 *BaseAddress
= CapturedBaseAddress
;
2433 *NumberOfBytesToLock
= 0;
2435 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2438 // Get exception code
2440 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2447 return STATUS_SUCCESS
;
2452 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
2453 IN OUT PVOID
*BaseAddress
,
2454 IN OUT PSIZE_T NumberOfBytesToUnlock
,
2458 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2460 BOOLEAN Attached
= FALSE
;
2461 KAPC_STATE ApcState
;
2462 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2463 PVOID CapturedBaseAddress
;
2464 SIZE_T CapturedBytesToUnlock
;
2470 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2473 // Invalid set of flags
2475 return STATUS_INVALID_PARAMETER
;
2479 // At least one flag must be specified
2481 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2486 return STATUS_INVALID_PARAMETER
;
2490 // Enter SEH for probing
2495 // Validate output data
2497 ProbeForWritePointer(BaseAddress
);
2498 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
2503 CapturedBaseAddress
= *BaseAddress
;
2504 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
2506 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2509 // Get exception code
2511 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2516 // Catch illegal base address
2518 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2521 // Catch illegal region size
2523 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
2528 return STATUS_INVALID_PARAMETER
;
2532 // 0 is also illegal
2534 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
2537 // Get a reference to the process
2539 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2540 PROCESS_VM_OPERATION
,
2545 if (!NT_SUCCESS(Status
)) return Status
;
2548 // Check if this is is system-mapped
2550 if (MapType
& MAP_SYSTEM
)
2553 // Check for required privilege
2555 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2558 // Fail: Don't have it
2560 ObDereferenceObject(Process
);
2561 return STATUS_PRIVILEGE_NOT_HELD
;
2566 // Check if we should attach
2568 if (CurrentProcess
!= Process
)
2573 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2585 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2588 // Release reference
2590 ObDereferenceObject(Process
);
2593 // Enter SEH to return data
2598 // Return data to user
2600 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2601 *NumberOfBytesToUnlock
= 0;
2603 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2606 // Get exception code
2608 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2615 return STATUS_SUCCESS
;
2620 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
2621 IN OUT PVOID
*BaseAddress
,
2622 IN OUT PSIZE_T NumberOfBytesToFlush
,
2623 OUT PIO_STATUS_BLOCK IoStatusBlock
)
2627 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2628 PVOID CapturedBaseAddress
;
2629 SIZE_T CapturedBytesToFlush
;
2630 IO_STATUS_BLOCK LocalStatusBlock
;
2634 // Check if we came from user mode
2636 if (PreviousMode
!= KernelMode
)
2639 // Enter SEH for probing
2644 // Validate all outputs
2646 ProbeForWritePointer(BaseAddress
);
2647 ProbeForWriteSize_t(NumberOfBytesToFlush
);
2648 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2653 CapturedBaseAddress
= *BaseAddress
;
2654 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2659 // Get exception code
2661 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2670 CapturedBaseAddress
= *BaseAddress
;
2671 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2675 // Catch illegal base address
2677 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2680 // Catch illegal region size
2682 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2687 return STATUS_INVALID_PARAMETER
;
2691 // Get a reference to the process
2693 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2694 PROCESS_VM_OPERATION
,
2699 if (!NT_SUCCESS(Status
)) return Status
;
2704 Status
= MmFlushVirtualMemory(Process
,
2705 &CapturedBaseAddress
,
2706 &CapturedBytesToFlush
,
2710 // Release reference
2712 ObDereferenceObject(Process
);
2715 // Enter SEH to return data
2720 // Return data to user
2722 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2723 *NumberOfBytesToFlush
= 0;
2724 *IoStatusBlock
= LocalStatusBlock
;
2726 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2742 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2744 IN PVOID BaseAddress
,
2745 IN SIZE_T RegionSize
,
2746 IN PVOID
*UserAddressArray
,
2747 OUT PULONG_PTR EntriesInUserAddressArray
,
2748 OUT PULONG Granularity
)
2753 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2754 ULONG_PTR CapturedEntryCount
;
2758 // Check if we came from user mode
2760 if (PreviousMode
!= KernelMode
)
2763 // Enter SEH for probing
2768 // Catch illegal base address
2770 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2773 // Catch illegal region size
2775 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2780 return STATUS_INVALID_PARAMETER_3
;
2784 // Validate all data
2786 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2787 ProbeForWriteUlong(Granularity
);
2792 CapturedEntryCount
= *EntriesInUserAddressArray
;
2795 // Must have a count
2797 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2800 // Can't be larger than the maximum
2802 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2807 return STATUS_INVALID_PARAMETER_5
;
2811 // Probe the actual array
2813 ProbeForWrite(UserAddressArray
,
2814 CapturedEntryCount
* sizeof(PVOID
),
2817 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2820 // Get exception code
2822 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2831 CapturedEntryCount
= *EntriesInUserAddressArray
;
2832 ASSERT(CapturedEntryCount
!= 0);
2836 // Check if this is a local request
2838 if (ProcessHandle
== NtCurrentProcess())
2841 // No need to reference the process
2843 Process
= PsGetCurrentProcess();
2848 // Reference the target
2850 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2851 PROCESS_VM_OPERATION
,
2856 if (!NT_SUCCESS(Status
)) return Status
;
2860 // Compute the last address and validate it
2862 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2863 if (BaseAddress
> EndAddress
)
2868 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2869 return STATUS_INVALID_PARAMETER_4
;
2878 // Dereference if needed
2880 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2883 // Enter SEH to return data
2888 // Return data to user
2890 *EntriesInUserAddressArray
= 0;
2891 *Granularity
= PAGE_SIZE
;
2893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2896 // Get exception code
2898 Status
= _SEH2_GetExceptionCode();
2905 return STATUS_SUCCESS
;
2913 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2914 IN PVOID BaseAddress
,
2915 IN SIZE_T RegionSize
)
2920 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2921 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2924 // Catch illegal base address
2926 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2929 // Catch illegal region size
2931 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2936 return STATUS_INVALID_PARAMETER_3
;
2940 // Check if this is a local request
2942 if (ProcessHandle
== NtCurrentProcess())
2945 // No need to reference the process
2947 Process
= PsGetCurrentProcess();
2952 // Reference the target
2954 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2955 PROCESS_VM_OPERATION
,
2960 if (!NT_SUCCESS(Status
)) return Status
;
2964 // Compute the last address and validate it
2966 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2967 if (BaseAddress
> EndAddress
)
2972 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2973 return STATUS_INVALID_PARAMETER_3
;
2982 // Dereference if needed
2984 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2989 return STATUS_SUCCESS
;
2994 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2995 IN PVOID BaseAddress
,
2996 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2997 OUT PVOID MemoryInformation
,
2998 IN SIZE_T MemoryInformationLength
,
2999 OUT PSIZE_T ReturnLength
)
3001 NTSTATUS Status
= STATUS_SUCCESS
;
3002 KPROCESSOR_MODE PreviousMode
;
3004 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3006 /* Bail out if the address is invalid */
3007 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3009 /* Probe return buffer */
3010 PreviousMode
= ExGetPreviousMode();
3011 if (PreviousMode
!= KernelMode
)
3015 ProbeForWrite(MemoryInformation
,
3016 MemoryInformationLength
,
3019 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3021 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3023 Status
= _SEH2_GetExceptionCode();
3027 if (!NT_SUCCESS(Status
))
3033 switch(MemoryInformationClass
)
3035 case MemoryBasicInformation
:
3036 /* Validate the size information of the class */
3037 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3039 /* The size is invalid */
3040 return STATUS_INFO_LENGTH_MISMATCH
;
3042 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3045 MemoryInformationLength
,
3049 case MemorySectionName
:
3050 /* Validate the size information of the class */
3051 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3053 /* The size is invalid */
3054 return STATUS_INFO_LENGTH_MISMATCH
;
3056 Status
= MiQueryMemorySectionName(ProcessHandle
,
3059 MemoryInformationLength
,
3062 case MemoryWorkingSetList
:
3063 case MemoryBasicVlmInformation
:
3065 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3077 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3078 IN OUT PVOID
* UBaseAddress
,
3079 IN ULONG_PTR ZeroBits
,
3080 IN OUT PSIZE_T URegionSize
,
3081 IN ULONG AllocationType
,
3085 PMEMORY_AREA MemoryArea
;
3086 PFN_NUMBER PageCount
;
3087 PMMVAD Vad
, FoundVad
;
3088 PUSHORT UsedPageTableEntries
;
3090 PMMSUPPORT AddressSpace
;
3092 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3093 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3094 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3095 PETHREAD CurrentThread
= PsGetCurrentThread();
3096 KAPC_STATE ApcState
;
3097 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3098 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3100 PMMPTE PointerPte
, PointerPde
, LastPte
;
3103 /* Check for valid Zero bits */
3106 DPRINT1("Too many zero bits\n");
3107 return STATUS_INVALID_PARAMETER_3
;
3110 /* Check for valid Allocation Types */
3111 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3112 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3114 DPRINT1("Invalid Allocation Type\n");
3115 return STATUS_INVALID_PARAMETER_5
;
3118 /* Check for at least one of these Allocation Types to be set */
3119 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3121 DPRINT1("No memory allocation base type\n");
3122 return STATUS_INVALID_PARAMETER_5
;
3125 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3126 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3128 DPRINT1("Invalid use of MEM_RESET\n");
3129 return STATUS_INVALID_PARAMETER_5
;
3132 /* Check if large pages are being used */
3133 if (AllocationType
& MEM_LARGE_PAGES
)
3135 /* Large page allocations MUST be committed */
3136 if (!(AllocationType
& MEM_COMMIT
))
3138 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3139 return STATUS_INVALID_PARAMETER_5
;
3142 /* These flags are not allowed with large page allocations */
3143 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3145 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3146 return STATUS_INVALID_PARAMETER_5
;
3150 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3151 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3153 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3154 return STATUS_INVALID_PARAMETER_5
;
3157 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3158 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3160 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3161 return STATUS_INVALID_PARAMETER_5
;
3164 /* Check for valid MEM_PHYSICAL usage */
3165 if (AllocationType
& MEM_PHYSICAL
)
3167 /* Only these flags are allowed with MEM_PHYSIAL */
3168 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3170 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3171 return STATUS_INVALID_PARAMETER_5
;
3174 /* Then make sure PAGE_READWRITE is used */
3175 if (Protect
!= PAGE_READWRITE
)
3177 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3178 return STATUS_INVALID_PARAMETER_6
;
3183 // Force PAGE_READWRITE for everything, for now
3185 Protect
= PAGE_READWRITE
;
3187 /* Calculate the protection mask and make sure it's valid */
3188 ProtectionMask
= MiMakeProtectionMask(Protect
);
3189 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3191 DPRINT1("Invalid protection mask\n");
3192 return STATUS_INVALID_PAGE_PROTECTION
;
3198 /* Check for user-mode parameters */
3199 if (PreviousMode
!= KernelMode
)
3201 /* Make sure they are writable */
3202 ProbeForWritePointer(UBaseAddress
);
3203 ProbeForWriteUlong(URegionSize
);
3206 /* Capture their values */
3207 PBaseAddress
= *UBaseAddress
;
3208 PRegionSize
= *URegionSize
;
3210 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3212 /* Return the exception code */
3213 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3217 /* Make sure the allocation isn't past the VAD area */
3218 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3220 DPRINT1("Virtual allocation base above User Space\n");
3221 return STATUS_INVALID_PARAMETER_2
;
3224 /* Make sure the allocation wouldn't overflow past the VAD area */
3225 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3227 DPRINT1("Region size would overflow into kernel-memory\n");
3228 return STATUS_INVALID_PARAMETER_4
;
3231 /* Make sure there's a size specified */
3234 DPRINT1("Region size is invalid (zero)\n");
3235 return STATUS_INVALID_PARAMETER_4
;
3239 // If this is for the current process, just use PsGetCurrentProcess
3241 if (ProcessHandle
== NtCurrentProcess())
3243 Process
= CurrentProcess
;
3248 // Otherwise, reference the process with VM rights and attach to it if
3249 // this isn't the current process. We must attach because we'll be touching
3250 // PTEs and PDEs that belong to user-mode memory, and also touching the
3251 // Working Set which is stored in Hyperspace.
3253 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3254 PROCESS_VM_OPERATION
,
3259 if (!NT_SUCCESS(Status
)) return Status
;
3260 if (CurrentProcess
!= Process
)
3262 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3268 // Check for large page allocations and make sure that the required privilege
3269 // is being held, before attempting to handle them.
3271 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3272 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3274 /* Fail without it */
3275 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3276 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3277 goto FailPathNoLock
;
3281 // Assert on the things we don't yet support
3283 ASSERT(ZeroBits
== 0);
3284 ASSERT((AllocationType
& MEM_LARGE_PAGES
) == 0);
3285 ASSERT((AllocationType
& MEM_PHYSICAL
) == 0);
3286 ASSERT((AllocationType
& MEM_WRITE_WATCH
) == 0);
3287 ASSERT((AllocationType
& MEM_TOP_DOWN
) == 0);
3288 ASSERT((AllocationType
& MEM_RESET
) == 0);
3289 ASSERT(Process
->VmTopDown
== 0);
3292 // Check if the caller is reserving memory, or committing memory and letting
3293 // us pick the base address
3295 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3298 // Do not allow COPY_ON_WRITE through this API
3300 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3302 DPRINT1("Copy on write not allowed through this path\n");
3303 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3304 goto FailPathNoLock
;
3308 // Does the caller have an address in mind, or is this a blind commit?
3313 // This is a blind commit, all we need is the region size
3315 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3316 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3318 StartingAddress
= 0;
3323 // This is a reservation, so compute the starting address on the
3324 // expected 64KB granularity, and see where the ending address will
3325 // fall based on the aligned address and the passed in region size
3327 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3328 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3329 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3333 // Allocate and initialize the VAD
3335 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3336 ASSERT(Vad
!= NULL
);
3337 Vad
->u
.LongFlags
= 0;
3338 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3339 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3340 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3341 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3344 // Lock the address space and make sure the process isn't already dead
3346 AddressSpace
= MmGetCurrentAddressSpace();
3347 MmLockAddressSpace(AddressSpace
);
3348 if (Process
->VmDeleted
)
3350 Status
= STATUS_PROCESS_IS_TERMINATING
;
3355 // Did we have a base address? If no, find a valid address that is 64KB
3356 // aligned in the VAD tree. Otherwise, make sure that the address range
3357 // which was passed in isn't already conflicting with an existing address
3362 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3365 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3367 if (!NT_SUCCESS(Status
)) goto FailPath
;
3370 // Now we know where the allocation ends. Make sure it doesn't end up
3371 // somewhere in kernel mode.
3373 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3374 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3376 Status
= STATUS_NO_MEMORY
;
3380 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3381 EndingAddress
>> PAGE_SHIFT
,
3385 // The address specified is in conflict!
3387 Status
= STATUS_CONFLICTING_ADDRESSES
;
3392 // Write out the VAD fields for this allocation
3394 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3395 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3398 // FIXME: Should setup VAD bitmap
3400 Status
= STATUS_SUCCESS
;
3403 // Lock the working set and insert the VAD into the process VAD tree
3405 MiLockProcessWorkingSet(Process
, CurrentThread
);
3406 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3407 MiInsertVad(Vad
, Process
);
3408 MiUnlockProcessWorkingSet(Process
, CurrentThread
);
3411 // Update the virtual size of the process, and if this is now the highest
3412 // virtual size we have ever seen, update the peak virtual size to reflect
3415 Process
->VirtualSize
+= PRegionSize
;
3416 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
3418 Process
->PeakVirtualSize
= Process
->VirtualSize
;
3422 // Release address space and detach and dereference the target process if
3423 // it was different from the current process
3425 MmUnlockAddressSpace(AddressSpace
);
3426 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3427 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3430 // Use SEH to write back the base address and the region size. In the case
3431 // of an exception, we do not return back the exception code, as the memory
3432 // *has* been allocated. The caller would now have to call VirtualQuery
3433 // or do some other similar trick to actually find out where its memory
3434 // allocation ended up
3438 *URegionSize
= PRegionSize
;
3439 *UBaseAddress
= (PVOID
)StartingAddress
;
3441 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3445 return STATUS_SUCCESS
;
3449 // This is a MEM_COMMIT on top of an existing address which must have been
3450 // MEM_RESERVED already. Compute the start and ending base addresses based
3451 // on the user input, and then compute the actual region size once all the
3452 // alignments have been done.
3454 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
3455 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
3456 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
3459 // Lock the address space and make sure the process isn't already dead
3461 AddressSpace
= MmGetCurrentAddressSpace();
3462 MmLockAddressSpace(AddressSpace
);
3463 if (Process
->VmDeleted
)
3465 DPRINT1("Process is dying\n");
3466 Status
= STATUS_PROCESS_IS_TERMINATING
;
3471 // Get the VAD for this address range, and make sure it exists
3473 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3474 EndingAddress
>> PAGE_SHIFT
,
3478 DPRINT1("Could not find a VAD for this allocation\n");
3479 Status
= STATUS_CONFLICTING_ADDRESSES
;
3484 // These kinds of VADs are illegal for this Windows function when trying to
3485 // commit an existing range
3487 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
3488 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
3489 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
3491 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
3492 Status
= STATUS_CONFLICTING_ADDRESSES
;
3497 // Make sure that this address range actually fits within the VAD for it
3499 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
3500 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
3502 DPRINT1("Address range does not fit into the VAD\n");
3503 Status
= STATUS_CONFLICTING_ADDRESSES
;
3508 // If this is an existing section view, we call the old RosMm routine which
3509 // has the relevant code required to handle the section scenario. In the future
3510 // we will limit this even more so that there's almost nothing that the code
3511 // needs to do, and it will become part of section.c in RosMm
3513 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
3514 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
3516 return MiRosAllocateVirtualMemory(ProcessHandle
,
3527 // Is this a previously reserved section being committed? If so, enter the
3528 // special section path
3530 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
3533 // You cannot commit large page sections through this API
3535 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
3537 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
3538 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3543 // You can only use caching flags on a rotate VAD
3545 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
3546 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
3548 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
3549 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3554 // We should make sure that the section's permissions aren't being messed with
3556 if (FoundVad
->u
.VadFlags
.NoChange
)
3558 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
3562 // ARM3 does not support file-backed sections, only shared memory
3564 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
3567 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
3569 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
3570 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
3572 DPRINT1("Invalid page protection for rotate VAD\n");
3573 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3578 // Compute PTE addresses and the quota charge, then grab the commit lock
3580 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
3581 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
3582 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
3583 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
3586 // Get the segment template PTE and start looping each page
3588 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
3589 ASSERT(TempPte
.u
.Long
!= 0);
3590 while (PointerPte
<= LastPte
)
3593 // For each non-already-committed page, write the invalid template PTE
3595 if (PointerPte
->u
.Long
== 0)
3597 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3607 // Now do the commit accounting and release the lock
3609 ASSERT(QuotaCharge
>= QuotaFree
);
3610 QuotaCharge
-= QuotaFree
;
3611 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
3612 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
3615 // We are done with committing the section pages
3617 Status
= STATUS_SUCCESS
;
3622 // This is a specific ReactOS check because we only use normal VADs
3624 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
3627 // While this is an actual Windows check
3629 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
3632 // Throw out attempts to use copy-on-write through this API path
3634 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3636 DPRINT1("Write copy attempted when not allowed\n");
3637 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3642 // Initialize a demand-zero PTE
3645 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
3648 // Get the PTE, PDE and the last PTE for this address range
3650 PointerPde
= MiAddressToPde(StartingAddress
);
3651 PointerPte
= MiAddressToPte(StartingAddress
);
3652 LastPte
= MiAddressToPte(EndingAddress
);
3655 // Update the commit charge in the VAD as well as in the process, and check
3656 // if this commit charge was now higher than the last recorded peak, in which
3657 // case we also update the peak
3659 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
3660 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
3661 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
3663 Process
->CommitChargePeak
= Process
->CommitCharge
;
3667 // Lock the working set while we play with user pages and page tables
3669 //MiLockWorkingSet(CurrentThread, AddressSpace);
3672 // Make the current page table valid, and then loop each page within it
3674 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
3675 while (PointerPte
<= LastPte
)
3678 // Have we crossed into a new page table?
3680 if (!(((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)))
3683 // Get the PDE and now make it valid too
3685 PointerPde
= MiAddressToPte(PointerPte
);
3686 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
3690 // Is this a zero PTE as expected?
3692 if (PointerPte
->u
.Long
== 0)
3695 // First increment the count of pages in the page table for this
3698 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
3699 (*UsedPageTableEntries
)++;
3700 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
3703 // And now write the invalid demand-zero PTE as requested
3705 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3707 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
3710 // If the PTE was already decommitted, there is nothing else to do
3711 // but to write the new demand-zero PTE
3713 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3715 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
3718 // We don't handle these scenarios yet
3720 if (PointerPte
->u
.Soft
.Valid
== 0)
3722 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
3723 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
3727 // There's a change in protection, remember this for later, but do
3728 // not yet handle it.
3730 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect
);
3731 ChangeProtection
= TRUE
;
3735 // Move to the next PTE
3741 // This path is not yet handled
3743 ASSERT(ChangeProtection
== FALSE
);
3746 // Release the working set lock, unlock the address space, and detach from
3747 // the target process if it was not the current process. Also dereference the
3748 // target process if this wasn't the case.
3750 //MiUnlockProcessWorkingSet(Process, CurrentThread);
3751 Status
= STATUS_SUCCESS
;
3753 MmUnlockAddressSpace(AddressSpace
);
3755 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3756 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3759 // Use SEH to write back the base address and the region size. In the case
3760 // of an exception, we strangely do return back the exception code, even
3761 // though the memory *has* been allocated. This mimics Windows behavior and
3762 // there is not much we can do about it.
3766 *URegionSize
= PRegionSize
;
3767 *UBaseAddress
= (PVOID
)StartingAddress
;
3769 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3771 Status
= _SEH2_GetExceptionCode();
3782 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
3783 IN PVOID
* UBaseAddress
,
3784 IN PSIZE_T URegionSize
,
3787 PMEMORY_AREA MemoryArea
;
3790 ULONG_PTR CommitReduction
= 0;
3791 ULONG_PTR StartingAddress
, EndingAddress
;
3795 PMMSUPPORT AddressSpace
;
3796 PETHREAD CurrentThread
= PsGetCurrentThread();
3797 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3798 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3799 KAPC_STATE ApcState
;
3800 BOOLEAN Attached
= FALSE
;
3804 // Only two flags are supported
3806 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
3808 DPRINT1("Invalid FreeType\n");
3809 return STATUS_INVALID_PARAMETER_4
;
3813 // Check if no flag was used, or if both flags were used
3815 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
3816 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
3818 DPRINT1("Invalid FreeType combination\n");
3819 return STATUS_INVALID_PARAMETER_4
;
3823 // Enter SEH for probe and capture. On failure, return back to the caller
3824 // with an exception violation.
3829 // Check for user-mode parameters and make sure that they are writeable
3831 if (PreviousMode
!= KernelMode
)
3833 ProbeForWritePointer(UBaseAddress
);
3834 ProbeForWriteUlong(URegionSize
);
3838 // Capture the current values
3840 PBaseAddress
= *UBaseAddress
;
3841 PRegionSize
= *URegionSize
;
3843 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3845 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3850 // Make sure the allocation isn't past the user area
3852 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
3854 DPRINT1("Virtual free base above User Space\n");
3855 return STATUS_INVALID_PARAMETER_2
;
3859 // Make sure the allocation wouldn't overflow past the user area
3861 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3863 DPRINT1("Region size would overflow into kernel-memory\n");
3864 return STATUS_INVALID_PARAMETER_3
;
3868 // If this is for the current process, just use PsGetCurrentProcess
3870 if (ProcessHandle
== NtCurrentProcess())
3872 Process
= CurrentProcess
;
3877 // Otherwise, reference the process with VM rights and attach to it if
3878 // this isn't the current process. We must attach because we'll be touching
3879 // PTEs and PDEs that belong to user-mode memory, and also touching the
3880 // Working Set which is stored in Hyperspace.
3882 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3883 PROCESS_VM_OPERATION
,
3888 if (!NT_SUCCESS(Status
)) return Status
;
3889 if (CurrentProcess
!= Process
)
3891 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3897 // Lock the address space
3899 AddressSpace
= MmGetCurrentAddressSpace();
3900 MmLockAddressSpace(AddressSpace
);
3903 // If the address space is being deleted, fail the de-allocation since it's
3904 // too late to do anything about it
3906 if (Process
->VmDeleted
)
3908 DPRINT1("Process is dead\n");
3909 Status
= STATUS_PROCESS_IS_TERMINATING
;
3914 // Compute start and end addresses, and locate the VAD
3916 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
3917 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3918 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
3921 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
3922 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
3927 // If the range exceeds the VAD's ending VPN, fail this request
3929 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
3931 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
3932 Status
= STATUS_UNABLE_TO_FREE_VM
;
3937 // These ASSERTs are here because ReactOS ARM3 does not currently implement
3938 // any other kinds of VADs.
3940 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
== 1);
3941 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
3942 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
3945 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
3946 // and that is is an ARM3 memory area, and not a section view, as we currently
3947 // don't support freeing those though this interface.
3949 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
3951 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
3954 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
3956 if (FreeType
& MEM_RELEASE
)
3959 // Is the caller trying to remove the whole VAD, or remove only a portion
3960 // of it? If no region size is specified, then the assumption is that the
3961 // whole VAD is to be destroyed
3966 // The caller must specify the base address identically to the range
3967 // that is stored in the VAD.
3969 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
3971 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
3972 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
3977 // Now compute the actual start/end addresses based on the VAD
3979 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
3980 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
3983 // Finally lock the working set and remove the VAD from the VAD tree
3985 MiLockWorkingSet(CurrentThread
, AddressSpace
);
3986 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
3987 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
3992 // This means the caller wants to release a specific region within
3993 // the range. We have to find out which range this is -- the following
3994 // possibilities exist plus their union (CASE D):
3996 // STARTING ADDRESS ENDING ADDRESS
3997 // [<========][========================================][=========>]
3998 // CASE A CASE B CASE C
4001 // First, check for case A or D
4003 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4008 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4011 // This is the easiest one to handle -- it is identical to
4012 // the code path above when the caller sets a zero region size
4013 // and the whole VAD is destroyed
4015 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4016 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4017 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4022 // This case is pretty easy too -- we compute a bunch of
4023 // pages to decommit, and then push the VAD's starting address
4024 // a bit further down, then decrement the commit charge
4026 // NOT YET IMPLEMENTED IN ARM3.
4028 DPRINT1("Case A not handled\n");
4029 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4033 // After analyzing the VAD, set it to NULL so that we don't
4034 // free it in the exit path
4042 // This is case B or case C. First check for case C
4044 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4047 // This is pretty easy and similar to case A. We compute the
4048 // amount of pages to decommit, update the VAD's commit charge
4049 // and then change the ending address of the VAD to be a bit
4052 // NOT YET IMPLEMENTED IN ARM3.
4054 DPRINT1("Case C not handled\n");
4055 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4061 // This is case B and the hardest one. Because we are removing
4062 // a chunk of memory from the very middle of the VAD, we must
4063 // actually split the VAD into two new VADs and compute the
4064 // commit charges for each of them, and reinsert new charges.
4066 // NOT YET IMPLEMENTED IN ARM3.
4068 DPRINT1("Case B not handled\n");
4069 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4074 // After analyzing the VAD, set it to NULL so that we don't
4075 // free it in the exit path
4082 // Now we have a range of pages to dereference, so call the right API
4083 // to do that and then release the working set, since we're done messing
4084 // around with process pages.
4086 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4087 MiUnlockWorkingSet(CurrentThread
, AddressSpace
);
4088 Status
= STATUS_SUCCESS
;
4092 // Update the process counters
4094 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4095 Process
->CommitCharge
-= CommitReduction
;
4096 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4099 // Unlock the address space and free the VAD in failure cases. Next,
4100 // detach from the target process so we can write the region size and the
4101 // base address to the correct source process, and dereference the target
4104 MmUnlockAddressSpace(AddressSpace
);
4105 if (Vad
) ExFreePool(Vad
);
4106 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4107 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4110 // Use SEH to safely return the region size and the base address of the
4111 // deallocation. If we get an access violation, don't return a failure code
4112 // as the deallocation *has* happened. The caller will just have to figure
4113 // out another way to find out where it is (such as VirtualQuery).
4117 *URegionSize
= PRegionSize
;
4118 *UBaseAddress
= (PVOID
)StartingAddress
;
4120 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4128 // This is the decommit path. You cannot decommit from the following VADs in
4129 // Windows, so fail the vall
4131 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4132 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4133 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4135 DPRINT1("Trying to decommit from invalid VAD\n");
4136 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4141 // If the caller did not specify a region size, first make sure that this
4142 // region is actually committed. If it is, then compute the ending address
4143 // based on the VAD.
4147 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4149 DPRINT1("Decomitting non-committed memory\n");
4150 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4153 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4157 // Decommit the PTEs for the range plus the actual backing pages for the
4158 // range, then reduce that amount from the commit charge in the VAD
4160 CommitReduction
= MiAddressToPte(EndingAddress
) -
4161 MiAddressToPte(StartingAddress
) +
4163 MiDecommitPages((PVOID
)StartingAddress
,
4164 MiAddressToPte(EndingAddress
),
4167 ASSERT(CommitReduction
>= 0);
4168 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4169 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4172 // We are done, go to the exit path without freeing the VAD as it remains
4173 // valid since we have not released the allocation.
4176 Status
= STATUS_SUCCESS
;
4180 // In the failure path, we detach and derefernece the target process, and
4181 // return whatever failure code was sent.
4184 MmUnlockAddressSpace(AddressSpace
);
4185 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4186 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);