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 LockChange
= FALSE
;
39 /* Must be a non-pool page table, since those are double-mapped already */
40 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
41 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
42 (PageTableVirtualAddress
> MmPagedPoolEnd
));
44 /* Working set lock or PFN lock should be held */
45 ASSERT(KeAreAllApcsDisabled() == TRUE
);
47 /* Check if the page table is valid */
48 while (!MmIsAddressValid(PageTableVirtualAddress
))
51 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
52 if (!NT_SUCCESS(Status
))
54 /* This should not fail */
55 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
58 (ULONG_PTR
)CurrentProcess
,
59 (ULONG_PTR
)PageTableVirtualAddress
);
62 /* This flag will be useful later when we do better locking */
66 /* Let caller know what the lock state is */
72 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
76 BOOLEAN LockChange
= FALSE
;
78 /* Must be e kernel address */
79 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
81 /* Check if the page is valid */
82 while (!MmIsAddressValid(VirtualAddress
))
84 /* Release the PFN database */
85 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
88 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
89 if (!NT_SUCCESS(Status
))
91 /* This should not fail */
92 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
96 (ULONG_PTR
)VirtualAddress
);
99 /* This flag will be useful later when we do better locking */
102 /* Lock the PFN database */
103 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
106 /* Let caller know what the lock state is */
112 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
113 IN PFN_NUMBER PageCount
,
115 OUT PPFN_NUMBER ValidPages
)
117 PFN_NUMBER ActualPages
= 0;
118 PETHREAD CurrentThread
= PsGetCurrentThread();
120 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
122 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
124 /* Lock the system working set */
125 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
130 /* Make sure there's some data about the page */
131 if (PointerPte
->u
.Long
)
133 /* As always, only handle current ARM3 scenarios */
134 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
135 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
137 /* Normally this is one possibility -- freeing a valid page */
138 if (PointerPte
->u
.Hard
.Valid
)
140 /* Get the page PFN */
141 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
142 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
144 /* Should not have any working set data yet */
145 ASSERT(Pfn1
->u1
.WsIndex
== 0);
147 /* Actual valid, legitimate, pages */
148 if (ValidPages
) (*ValidPages
)++;
150 /* Get the page table entry */
151 PageTableIndex
= Pfn1
->u4
.PteFrame
;
152 Pfn2
= MiGetPfnEntry(PageTableIndex
);
154 /* Lock the PFN database */
155 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
157 /* Delete it the page */
158 MI_SET_PFN_DELETED(Pfn1
);
159 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
161 /* Decrement the page table too */
162 DPRINT("FIXME: ARM3 should decrement the pool PDE refcount for: %p\n", PageTableIndex
);
163 #if 0 // ARM3: Dont't trust this yet
164 MiDecrementShareCount(Pfn2
, PageTableIndex
);
167 /* Release the PFN database */
168 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
170 /* Destroy the PTE */
171 PointerPte
->u
.Long
= 0;
174 /* Actual legitimate pages */
180 * The only other ARM3 possibility is a demand zero page, which would
181 * mean freeing some of the paged pool pages that haven't even been
182 * touched yet, as part of a larger allocation.
184 * Right now, we shouldn't expect any page file information in the PTE
186 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
188 /* Destroy the PTE */
189 PointerPte
->u
.Long
= 0;
197 /* Release the working set */
198 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
200 /* Flush the entire TLB */
201 KeFlushEntireTb(TRUE
, TRUE
);
209 MiDeletePte(IN PMMPTE PointerPte
,
210 IN PVOID VirtualAddress
,
211 IN PEPROCESS CurrentProcess
,
212 IN PMMPTE PrototypePte
)
216 PFN_NUMBER PageFrameIndex
;
219 /* PFN lock must be held */
220 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
222 /* Capture the PTE */
223 TempPte
= *PointerPte
;
225 /* We only support valid PTEs for now */
226 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
227 if (TempPte
.u
.Hard
.Valid
== 0)
229 /* Invalid PTEs not supported yet */
230 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
231 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
234 /* Get the PFN entry */
235 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
236 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
238 /* Check if this is a valid, prototype PTE */
239 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
241 /* Get the PDE and make sure it's faulted in */
242 PointerPde
= MiAddressToPde(PointerPte
);
243 if (PointerPde
->u
.Hard
.Valid
== 0)
245 #if (_MI_PAGING_LEVELS == 2)
246 /* Could be paged pool access from a new process -- synchronize the page directories */
247 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
250 /* The PDE must be valid at this point */
251 KeBugCheckEx(MEMORY_MANAGEMENT
,
253 (ULONG_PTR
)PointerPte
,
255 (ULONG_PTR
)VirtualAddress
);
257 #if (_MI_PAGING_LEVELS == 2)
260 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
261 //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PTE(PointerPde));
263 /* Drop the share count */
264 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
267 if (PointerPte
<= MiHighestUserPte
) ASSERT(PrototypePte
== Pfn1
->PteAddress
);
271 /* Make sure the saved PTE address is valid */
272 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
274 /* The PFN entry is illegal, or invalid */
275 KeBugCheckEx(MEMORY_MANAGEMENT
,
277 (ULONG_PTR
)PointerPte
,
279 (ULONG_PTR
)Pfn1
->PteAddress
);
282 /* There should only be 1 shared reference count */
283 ASSERT(Pfn1
->u2
.ShareCount
== 1);
285 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
286 //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
288 /* Mark the PFN for deletion and dereference what should be the last ref */
289 MI_SET_PFN_DELETED(Pfn1
);
290 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
292 /* We should eventually do this */
293 //CurrentProcess->NumberOfPrivatePages--;
296 /* Destroy the PTE and flush the TLB */
297 PointerPte
->u
.Long
= 0;
303 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
304 IN ULONG_PTR EndingAddress
,
307 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
310 PEPROCESS CurrentProcess
;
312 BOOLEAN AddressGap
= FALSE
;
313 PSUBSECTION Subsection
;
315 /* Get out if this is a fake VAD, RosMm will free the marea pages */
316 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
318 /* Grab the process and PTE/PDE for the address being deleted */
319 CurrentProcess
= PsGetCurrentProcess();
320 PointerPde
= MiAddressToPde(Va
);
321 PointerPte
= MiAddressToPte(Va
);
323 /* Check if this is a section VAD or a VM VAD */
324 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
326 /* Don't worry about prototypes */
327 PrototypePte
= LastPrototypePte
= NULL
;
331 /* Get the prototype PTE */
332 PrototypePte
= Vad
->FirstPrototypePte
;
333 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
336 /* In all cases, we don't support fork() yet */
337 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
339 /* Loop the PTE for each VA */
342 /* First keep going until we find a valid PDE */
343 while (!PointerPde
->u
.Long
)
345 /* There are gaps in the address space */
348 /* Still no valid PDE, try the next 4MB (or whatever) */
351 /* Update the PTE on this new boundary */
352 PointerPte
= MiPteToAddress(PointerPde
);
354 /* Check if all the PDEs are invalid, so there's nothing to free */
355 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
356 if (Va
> EndingAddress
) return;
359 /* Now check if the PDE is mapped in */
360 if (!PointerPde
->u
.Hard
.Valid
)
362 /* It isn't, so map it in */
363 PointerPte
= MiPteToAddress(PointerPde
);
364 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
367 /* Now we should have a valid PDE, mapped in, and still have some VA */
368 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
369 ASSERT(Va
<= EndingAddress
);
371 /* Check if this is a section VAD with gaps in it */
372 if ((AddressGap
) && (LastPrototypePte
))
374 /* We need to skip to the next correct prototype PTE */
375 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
377 /* And we need the subsection to skip to the next last prototype PTE */
378 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
382 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
386 /* No more subsections, we are done with prototype PTEs */
391 /* Lock the PFN Database while we delete the PTEs */
392 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
395 /* Capture the PDE and make sure it exists */
396 TempPte
= *PointerPte
;
399 /* Check if the PTE is actually mapped in */
400 if (TempPte
.u
.Long
& 0xFFFFFC01)
402 /* Are we dealing with section VAD? */
403 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
405 /* We need to skip to the next correct prototype PTE */
406 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
408 /* And we need the subsection to skip to the next last prototype PTE */
409 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
413 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
417 /* No more subsections, we are done with prototype PTEs */
422 /* Check for prototype PTE */
423 if ((TempPte
.u
.Hard
.Valid
== 0) &&
424 (TempPte
.u
.Soft
.Prototype
== 1))
427 PointerPte
->u
.Long
= 0;
431 /* Delete the PTE proper */
432 MiDeletePte(PointerPte
,
440 /* The PTE was never mapped, just nuke it here */
441 PointerPte
->u
.Long
= 0;
445 /* Update the address and PTE for it */
450 /* Making sure the PDE is still valid */
451 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
453 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
455 /* The PDE should still be valid at this point */
456 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
458 /* Release the lock and get out if we're done */
459 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
460 if (Va
> EndingAddress
) return;
462 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
463 PointerPde
= MiAddressToPde(Va
);
469 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
470 OUT PBOOLEAN HaveBadAddress
,
471 OUT PULONG_PTR BadAddress
)
473 PEXCEPTION_RECORD ExceptionRecord
;
479 *HaveBadAddress
= FALSE
;
482 // Get the exception record
484 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
487 // Look at the exception code
489 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
490 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
491 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
494 // We can tell the address if we have more than one parameter
496 if (ExceptionRecord
->NumberParameters
> 1)
499 // Return the address
501 *HaveBadAddress
= TRUE
;
502 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
507 // Continue executing the next handler
509 return EXCEPTION_EXECUTE_HANDLER
;
514 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
515 IN PVOID SourceAddress
,
516 IN PEPROCESS TargetProcess
,
517 OUT PVOID TargetAddress
,
518 IN SIZE_T BufferSize
,
519 IN KPROCESSOR_MODE PreviousMode
,
520 OUT PSIZE_T ReturnSize
)
522 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
523 PMDL Mdl
= (PMDL
)MdlBuffer
;
524 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
525 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
526 volatile BOOLEAN PagesLocked
;
527 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
528 volatile PVOID MdlAddress
;
530 BOOLEAN HaveBadAddress
;
531 ULONG_PTR BadAddress
;
532 NTSTATUS Status
= STATUS_SUCCESS
;
536 // Calculate the maximum amount of data to move
538 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
539 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
540 CurrentSize
= TotalSize
;
541 RemainingSize
= BufferSize
;
544 // Loop as long as there is still data
546 while (RemainingSize
> 0)
549 // Check if this transfer will finish everything off
551 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
554 // Attach to the source address space
556 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
559 // Reset state for this pass
563 FailedInMoving
= FALSE
;
564 ASSERT(FailedInProbe
== FALSE
);
567 // Protect user-mode copy
572 // If this is our first time, probe the buffer
574 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
577 // Catch a failure here
579 FailedInProbe
= TRUE
;
584 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
589 FailedInProbe
= FALSE
;
593 // Initialize and probe and lock the MDL
595 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
596 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
602 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
611 // Use our SEH handler to pick this up
613 FailedInMapping
= TRUE
;
614 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
618 // Now let go of the source and grab to the target process
620 KeUnstackDetachProcess(&ApcState
);
621 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
624 // Check if this is our first time through
626 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
629 // Catch a failure here
631 FailedInProbe
= TRUE
;
636 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
641 FailedInProbe
= FALSE
;
645 // Now do the actual move
647 FailedInMoving
= TRUE
;
648 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
650 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
655 // Detach from whoever we may be attached to
657 KeUnstackDetachProcess(&ApcState
);
660 // Check if we had mapped the pages
662 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
665 // Check if we had locked the pages
667 if (PagesLocked
) MmUnlockPages(Mdl
);
670 // Check if we hit working set quota
672 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
677 return STATUS_WORKING_SET_QUOTA
;
681 // Check if we failed during the probe or mapping
683 if ((FailedInProbe
) || (FailedInMapping
))
688 Status
= _SEH2_GetExceptionCode();
689 _SEH2_YIELD(return Status
);
693 // Otherwise, we failed probably during the move
695 *ReturnSize
= BufferSize
- RemainingSize
;
699 // Check if we know exactly where we stopped copying
704 // Return the exact number of bytes copied
706 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
711 // Return partial copy
713 Status
= STATUS_PARTIAL_COPY
;
718 // Check for SEH status
720 if (Status
!= STATUS_SUCCESS
) return Status
;
723 // Detach from target
725 KeUnstackDetachProcess(&ApcState
);
730 MmUnmapLockedPages(MdlAddress
, Mdl
);
734 // Update location and size
736 RemainingSize
-= CurrentSize
;
737 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
738 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
744 *ReturnSize
= BufferSize
;
745 return STATUS_SUCCESS
;
750 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
751 IN PVOID SourceAddress
,
752 IN PEPROCESS TargetProcess
,
753 OUT PVOID TargetAddress
,
754 IN SIZE_T BufferSize
,
755 IN KPROCESSOR_MODE PreviousMode
,
756 OUT PSIZE_T ReturnSize
)
758 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
759 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
760 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
761 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
764 BOOLEAN HaveBadAddress
;
765 ULONG_PTR BadAddress
;
766 NTSTATUS Status
= STATUS_SUCCESS
;
770 // Calculate the maximum amount of data to move
772 TotalSize
= MI_MAX_TRANSFER_SIZE
;
773 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
774 CurrentSize
= TotalSize
;
775 RemainingSize
= BufferSize
;
778 // Check if we can use the stack
780 if (BufferSize
<= MI_POOL_COPY_BYTES
)
785 PoolAddress
= (PVOID
)StackBuffer
;
792 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
793 if (!PoolAddress
) ASSERT(FALSE
);
794 HavePoolAddress
= TRUE
;
798 // Loop as long as there is still data
800 while (RemainingSize
> 0)
803 // Check if this transfer will finish everything off
805 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
808 // Attach to the source address space
810 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
813 // Reset state for this pass
815 FailedInMoving
= FALSE
;
816 ASSERT(FailedInProbe
== FALSE
);
819 // Protect user-mode copy
824 // If this is our first time, probe the buffer
826 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
829 // Catch a failure here
831 FailedInProbe
= TRUE
;
836 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
841 FailedInProbe
= FALSE
;
847 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
850 // Now let go of the source and grab to the target process
852 KeUnstackDetachProcess(&ApcState
);
853 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
856 // Check if this is our first time through
858 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
861 // Catch a failure here
863 FailedInProbe
= TRUE
;
868 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
873 FailedInProbe
= FALSE
;
877 // Now do the actual move
879 FailedInMoving
= TRUE
;
880 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
882 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
887 // Detach from whoever we may be attached to
889 KeUnstackDetachProcess(&ApcState
);
892 // Check if we had allocated pool
894 if (HavePoolAddress
) ExFreePool(PoolAddress
);
897 // Check if we failed during the probe
904 Status
= _SEH2_GetExceptionCode();
905 _SEH2_YIELD(return Status
);
909 // Otherwise, we failed, probably during the move
911 *ReturnSize
= BufferSize
- RemainingSize
;
915 // Check if we know exactly where we stopped copying
920 // Return the exact number of bytes copied
922 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
927 // Return partial copy
929 Status
= STATUS_PARTIAL_COPY
;
934 // Check for SEH status
936 if (Status
!= STATUS_SUCCESS
) return Status
;
939 // Detach from target
941 KeUnstackDetachProcess(&ApcState
);
944 // Update location and size
946 RemainingSize
-= CurrentSize
;
947 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
948 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
953 // Check if we had allocated pool
955 if (HavePoolAddress
) ExFreePool(PoolAddress
);
960 *ReturnSize
= BufferSize
;
961 return STATUS_SUCCESS
;
966 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
967 IN PVOID SourceAddress
,
968 IN PEPROCESS TargetProcess
,
969 OUT PVOID TargetAddress
,
970 IN SIZE_T BufferSize
,
971 IN KPROCESSOR_MODE PreviousMode
,
972 OUT PSIZE_T ReturnSize
)
975 PEPROCESS Process
= SourceProcess
;
978 // Don't accept zero-sized buffers
980 if (!BufferSize
) return STATUS_SUCCESS
;
983 // If we are copying from ourselves, lock the target instead
985 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
988 // Acquire rundown protection
990 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
995 return STATUS_PROCESS_IS_TERMINATING
;
999 // See if we should use the pool copy
1001 if (BufferSize
> MI_POOL_COPY_BYTES
)
1006 Status
= MiDoMappedCopy(SourceProcess
,
1019 Status
= MiDoPoolCopy(SourceProcess
,
1031 ExReleaseRundownProtection(&Process
->RundownProtect
);
1037 MmFlushVirtualMemory(IN PEPROCESS Process
,
1038 IN OUT PVOID
*BaseAddress
,
1039 IN OUT PSIZE_T RegionSize
,
1040 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1048 return STATUS_SUCCESS
;
1053 MiGetPageProtection(IN PMMPTE PointerPte
)
1059 /* Copy this PTE's contents */
1060 TempPte
= *PointerPte
;
1062 /* Assure it's not totally zero */
1063 ASSERT(TempPte
.u
.Long
);
1065 /* Check for a special prototype format */
1066 if (TempPte
.u
.Soft
.Valid
== 0 &&
1067 TempPte
.u
.Soft
.Prototype
== 1)
1069 /* Unsupported now */
1074 /* In the easy case of transition or demand zero PTE just return its protection */
1075 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1077 /* If we get here, the PTE is valid, so look up the page in PFN database */
1078 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1080 if (!Pfn
->u3
.e1
.PrototypePte
)
1082 /* Return protection of the original pte */
1083 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1086 /* This is hardware PTE */
1090 return PAGE_NOACCESS
;
1095 MiQueryAddressState(IN PVOID Va
,
1097 IN PEPROCESS TargetProcess
,
1098 OUT PULONG ReturnedProtect
,
1105 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1106 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1107 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1108 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1110 /* Only normal VADs supported */
1111 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1113 /* Get the PDE and PTE for the address */
1114 PointerPde
= MiAddressToPde(Va
);
1115 PointerPte
= MiAddressToPte(Va
);
1117 /* Return the next range */
1118 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1120 /* Loop to make sure the PDE is valid */
1126 /* Is the PDE empty? */
1127 if (!PointerPde
->u
.Long
)
1129 /* No address in this range used yet, move to the next PDE range */
1130 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1134 /* The PDE is not empty, but is it faulted in? */
1135 if (!PointerPde
->u
.Hard
.Valid
)
1137 /* It isn't, go ahead and do the fault */
1138 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1142 /* Check if the PDE was faulted in, making the PTE readable */
1143 if (!LockChange
) ValidPte
= TRUE
;
1144 } while (LockChange
);
1146 /* Is it safe to try reading the PTE? */
1149 /* FIXME: watch out for large pages */
1151 /* Capture the PTE */
1152 TempPte
= *PointerPte
;
1155 /* The PTE is valid, so it's not zeroed out */
1156 DemandZeroPte
= FALSE
;
1158 /* Check if it's valid or has a valid protection mask */
1159 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1160 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1161 (TempPte
.u
.Hard
.Valid
== 1))
1163 /* This means it's committed */
1166 /* Get protection state of this page */
1167 Protect
= MiGetPageProtection(PointerPte
);
1171 /* Otherwise our defaults should hold */
1172 ASSERT(Protect
== 0);
1173 ASSERT(State
== MEM_RESERVE
);
1178 /* Check if this was a demand-zero PTE, since we need to find the state */
1181 /* Check if the VAD is for committed memory */
1182 if (Vad
->u
.VadFlags
.MemCommit
)
1184 /* This is committed memory */
1187 /* Convert the protection */
1188 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1192 /* Return the protection code */
1193 *ReturnedProtect
= Protect
;
1197 /* PUBLIC FUNCTIONS ***********************************************************/
1204 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1215 MmSecureVirtualMemory(IN PVOID Address
,
1219 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1228 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1230 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1233 /* SYSTEM CALLS ***************************************************************/
1237 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1238 IN PVOID BaseAddress
,
1240 IN SIZE_T NumberOfBytesToRead
,
1241 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1243 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1245 NTSTATUS Status
= STATUS_SUCCESS
;
1246 SIZE_T BytesRead
= 0;
1250 // Check if we came from user mode
1252 if (PreviousMode
!= KernelMode
)
1255 // Validate the read addresses
1257 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1258 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1259 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1260 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1263 // Don't allow to write into kernel space
1265 return STATUS_ACCESS_VIOLATION
;
1269 // Enter SEH for probe
1274 // Probe the output value
1276 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1281 // Get exception code
1283 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1289 // Don't do zero-byte transfers
1291 if (NumberOfBytesToRead
)
1294 // Reference the process
1296 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1302 if (NT_SUCCESS(Status
))
1307 Status
= MmCopyVirtualMemory(Process
,
1309 PsGetCurrentProcess(),
1311 NumberOfBytesToRead
,
1316 // Dereference the process
1318 ObDereferenceObject(Process
);
1323 // Check if the caller sent this parameter
1325 if (NumberOfBytesRead
)
1328 // Enter SEH to guard write
1333 // Return the number of bytes read
1335 *NumberOfBytesRead
= BytesRead
;
1337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1351 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1352 IN PVOID BaseAddress
,
1354 IN SIZE_T NumberOfBytesToWrite
,
1355 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1357 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1359 NTSTATUS Status
= STATUS_SUCCESS
;
1360 SIZE_T BytesWritten
= 0;
1364 // Check if we came from user mode
1366 if (PreviousMode
!= KernelMode
)
1369 // Validate the read addresses
1371 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1372 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1373 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1374 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1377 // Don't allow to write into kernel space
1379 return STATUS_ACCESS_VIOLATION
;
1383 // Enter SEH for probe
1388 // Probe the output value
1390 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1395 // Get exception code
1397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1403 // Don't do zero-byte transfers
1405 if (NumberOfBytesToWrite
)
1408 // Reference the process
1410 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1416 if (NT_SUCCESS(Status
))
1421 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1425 NumberOfBytesToWrite
,
1430 // Dereference the process
1432 ObDereferenceObject(Process
);
1437 // Check if the caller sent this parameter
1439 if (NumberOfBytesWritten
)
1442 // Enter SEH to guard write
1447 // Return the number of bytes written
1449 *NumberOfBytesWritten
= BytesWritten
;
1451 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1465 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1466 IN OUT PVOID
*UnsafeBaseAddress
,
1467 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1468 IN ULONG NewAccessProtection
,
1469 OUT PULONG UnsafeOldAccessProtection
)
1472 ULONG OldAccessProtection
;
1474 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1475 PVOID BaseAddress
= NULL
;
1476 SIZE_T NumberOfBytesToProtect
= 0;
1477 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1479 BOOLEAN Attached
= FALSE
;
1480 KAPC_STATE ApcState
;
1484 // Check for valid protection flags
1486 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1487 if (Protection
!= PAGE_NOACCESS
&&
1488 Protection
!= PAGE_READONLY
&&
1489 Protection
!= PAGE_READWRITE
&&
1490 Protection
!= PAGE_WRITECOPY
&&
1491 Protection
!= PAGE_EXECUTE
&&
1492 Protection
!= PAGE_EXECUTE_READ
&&
1493 Protection
!= PAGE_EXECUTE_READWRITE
&&
1494 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1499 return STATUS_INVALID_PAGE_PROTECTION
;
1503 // Check if we came from user mode
1505 if (PreviousMode
!= KernelMode
)
1508 // Enter SEH for probing
1513 // Validate all outputs
1515 ProbeForWritePointer(UnsafeBaseAddress
);
1516 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1517 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1522 BaseAddress
= *UnsafeBaseAddress
;
1523 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1525 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1528 // Get exception code
1530 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1539 BaseAddress
= *UnsafeBaseAddress
;
1540 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1544 // Catch illegal base address
1546 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1549 // Catch illegal region size
1551 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1556 return STATUS_INVALID_PARAMETER_3
;
1560 // 0 is also illegal
1562 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1565 // Get a reference to the process
1567 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1568 PROCESS_VM_OPERATION
,
1573 if (!NT_SUCCESS(Status
)) return Status
;
1576 // Check if we should attach
1578 if (CurrentProcess
!= Process
)
1583 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1588 // Do the actual work
1590 Status
= MiProtectVirtualMemory(Process
,
1592 &NumberOfBytesToProtect
,
1593 NewAccessProtection
,
1594 &OldAccessProtection
);
1599 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1602 // Release reference
1604 ObDereferenceObject(Process
);
1607 // Enter SEH to return data
1612 // Return data to user
1614 *UnsafeOldAccessProtection
= OldAccessProtection
;
1615 *UnsafeBaseAddress
= BaseAddress
;
1616 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1631 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1632 IN OUT PVOID
*BaseAddress
,
1633 IN OUT PSIZE_T NumberOfBytesToLock
,
1637 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1639 BOOLEAN Attached
= FALSE
;
1640 KAPC_STATE ApcState
;
1641 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1642 PVOID CapturedBaseAddress
;
1643 SIZE_T CapturedBytesToLock
;
1649 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1652 // Invalid set of flags
1654 return STATUS_INVALID_PARAMETER
;
1658 // At least one flag must be specified
1660 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1665 return STATUS_INVALID_PARAMETER
;
1669 // Enter SEH for probing
1674 // Validate output data
1676 ProbeForWritePointer(BaseAddress
);
1677 ProbeForWriteSize_t(NumberOfBytesToLock
);
1682 CapturedBaseAddress
= *BaseAddress
;
1683 CapturedBytesToLock
= *NumberOfBytesToLock
;
1685 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1688 // Get exception code
1690 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1695 // Catch illegal base address
1697 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1700 // Catch illegal region size
1702 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1707 return STATUS_INVALID_PARAMETER
;
1711 // 0 is also illegal
1713 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1716 // Get a reference to the process
1718 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1719 PROCESS_VM_OPERATION
,
1724 if (!NT_SUCCESS(Status
)) return Status
;
1727 // Check if this is is system-mapped
1729 if (MapType
& MAP_SYSTEM
)
1732 // Check for required privilege
1734 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1737 // Fail: Don't have it
1739 ObDereferenceObject(Process
);
1740 return STATUS_PRIVILEGE_NOT_HELD
;
1745 // Check if we should attach
1747 if (CurrentProcess
!= Process
)
1752 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1764 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1767 // Release reference
1769 ObDereferenceObject(Process
);
1772 // Enter SEH to return data
1777 // Return data to user
1779 *BaseAddress
= CapturedBaseAddress
;
1780 *NumberOfBytesToLock
= 0;
1782 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1785 // Get exception code
1787 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1794 return STATUS_SUCCESS
;
1799 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1800 IN OUT PVOID
*BaseAddress
,
1801 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1805 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1807 BOOLEAN Attached
= FALSE
;
1808 KAPC_STATE ApcState
;
1809 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1810 PVOID CapturedBaseAddress
;
1811 SIZE_T CapturedBytesToUnlock
;
1817 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1820 // Invalid set of flags
1822 return STATUS_INVALID_PARAMETER
;
1826 // At least one flag must be specified
1828 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1833 return STATUS_INVALID_PARAMETER
;
1837 // Enter SEH for probing
1842 // Validate output data
1844 ProbeForWritePointer(BaseAddress
);
1845 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1850 CapturedBaseAddress
= *BaseAddress
;
1851 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1853 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1856 // Get exception code
1858 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1863 // Catch illegal base address
1865 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1868 // Catch illegal region size
1870 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1875 return STATUS_INVALID_PARAMETER
;
1879 // 0 is also illegal
1881 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1884 // Get a reference to the process
1886 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1887 PROCESS_VM_OPERATION
,
1892 if (!NT_SUCCESS(Status
)) return Status
;
1895 // Check if this is is system-mapped
1897 if (MapType
& MAP_SYSTEM
)
1900 // Check for required privilege
1902 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1905 // Fail: Don't have it
1907 ObDereferenceObject(Process
);
1908 return STATUS_PRIVILEGE_NOT_HELD
;
1913 // Check if we should attach
1915 if (CurrentProcess
!= Process
)
1920 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1932 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1935 // Release reference
1937 ObDereferenceObject(Process
);
1940 // Enter SEH to return data
1945 // Return data to user
1947 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1948 *NumberOfBytesToUnlock
= 0;
1950 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1953 // Get exception code
1955 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1962 return STATUS_SUCCESS
;
1967 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1968 IN OUT PVOID
*BaseAddress
,
1969 IN OUT PSIZE_T NumberOfBytesToFlush
,
1970 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1974 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1975 PVOID CapturedBaseAddress
;
1976 SIZE_T CapturedBytesToFlush
;
1977 IO_STATUS_BLOCK LocalStatusBlock
;
1981 // Check if we came from user mode
1983 if (PreviousMode
!= KernelMode
)
1986 // Enter SEH for probing
1991 // Validate all outputs
1993 ProbeForWritePointer(BaseAddress
);
1994 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1995 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2000 CapturedBaseAddress
= *BaseAddress
;
2001 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2003 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2006 // Get exception code
2008 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2017 CapturedBaseAddress
= *BaseAddress
;
2018 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2022 // Catch illegal base address
2024 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2027 // Catch illegal region size
2029 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2034 return STATUS_INVALID_PARAMETER
;
2038 // Get a reference to the process
2040 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2041 PROCESS_VM_OPERATION
,
2046 if (!NT_SUCCESS(Status
)) return Status
;
2051 Status
= MmFlushVirtualMemory(Process
,
2052 &CapturedBaseAddress
,
2053 &CapturedBytesToFlush
,
2057 // Release reference
2059 ObDereferenceObject(Process
);
2062 // Enter SEH to return data
2067 // Return data to user
2069 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2070 *NumberOfBytesToFlush
= 0;
2071 *IoStatusBlock
= LocalStatusBlock
;
2073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2089 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2091 IN PVOID BaseAddress
,
2092 IN SIZE_T RegionSize
,
2093 IN PVOID
*UserAddressArray
,
2094 OUT PULONG_PTR EntriesInUserAddressArray
,
2095 OUT PULONG Granularity
)
2100 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2101 ULONG_PTR CapturedEntryCount
;
2105 // Check if we came from user mode
2107 if (PreviousMode
!= KernelMode
)
2110 // Enter SEH for probing
2115 // Catch illegal base address
2117 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2120 // Catch illegal region size
2122 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2127 return STATUS_INVALID_PARAMETER_3
;
2131 // Validate all data
2133 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2134 ProbeForWriteUlong(Granularity
);
2139 CapturedEntryCount
= *EntriesInUserAddressArray
;
2142 // Must have a count
2144 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2147 // Can't be larger than the maximum
2149 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2154 return STATUS_INVALID_PARAMETER_5
;
2158 // Probe the actual array
2160 ProbeForWrite(UserAddressArray
,
2161 CapturedEntryCount
* sizeof(PVOID
),
2164 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2167 // Get exception code
2169 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2178 CapturedEntryCount
= *EntriesInUserAddressArray
;
2179 ASSERT(CapturedEntryCount
!= 0);
2183 // Check if this is a local request
2185 if (ProcessHandle
== NtCurrentProcess())
2188 // No need to reference the process
2190 Process
= PsGetCurrentProcess();
2195 // Reference the target
2197 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2198 PROCESS_VM_OPERATION
,
2203 if (!NT_SUCCESS(Status
)) return Status
;
2207 // Compute the last address and validate it
2209 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2210 if (BaseAddress
> EndAddress
)
2215 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2216 return STATUS_INVALID_PARAMETER_4
;
2225 // Dereference if needed
2227 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2230 // Enter SEH to return data
2235 // Return data to user
2237 *EntriesInUserAddressArray
= 0;
2238 *Granularity
= PAGE_SIZE
;
2240 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2243 // Get exception code
2245 Status
= _SEH2_GetExceptionCode();
2252 return STATUS_SUCCESS
;
2260 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2261 IN PVOID BaseAddress
,
2262 IN SIZE_T RegionSize
)
2267 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2268 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2271 // Catch illegal base address
2273 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2276 // Catch illegal region size
2278 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2283 return STATUS_INVALID_PARAMETER_3
;
2287 // Check if this is a local request
2289 if (ProcessHandle
== NtCurrentProcess())
2292 // No need to reference the process
2294 Process
= PsGetCurrentProcess();
2299 // Reference the target
2301 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2302 PROCESS_VM_OPERATION
,
2307 if (!NT_SUCCESS(Status
)) return Status
;
2311 // Compute the last address and validate it
2313 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2314 if (BaseAddress
> EndAddress
)
2319 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2320 return STATUS_INVALID_PARAMETER_3
;
2329 // Dereference if needed
2331 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2336 return STATUS_SUCCESS
;
2341 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
2342 IN PVOID BaseAddress
,
2343 OUT PVOID MemoryInformation
,
2344 IN SIZE_T MemoryInformationLength
,
2345 OUT PSIZE_T ReturnLength
)
2347 PEPROCESS TargetProcess
;
2348 NTSTATUS Status
= STATUS_SUCCESS
;
2350 PVOID Address
, NextAddress
;
2351 BOOLEAN Found
= FALSE
;
2352 ULONG NewProtect
, NewState
, BaseVpn
;
2353 MEMORY_BASIC_INFORMATION MemoryInfo
;
2354 KAPC_STATE ApcState
;
2355 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2356 PMEMORY_AREA MemoryArea
;
2357 SIZE_T ResultLength
;
2359 /* Check for illegal addresses in user-space, or the shared memory area */
2360 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2361 (PAGE_ALIGN(BaseAddress
) == (PVOID
)USER_SHARED_DATA
))
2363 Address
= PAGE_ALIGN(BaseAddress
);
2365 /* Make up an info structure describing this range */
2366 MemoryInfo
.BaseAddress
= Address
;
2367 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
2368 MemoryInfo
.Type
= MEM_PRIVATE
;
2370 /* Special case for shared data */
2371 if (Address
== (PVOID
)USER_SHARED_DATA
)
2373 MemoryInfo
.AllocationBase
= (PVOID
)USER_SHARED_DATA
;
2374 MemoryInfo
.State
= MEM_COMMIT
;
2375 MemoryInfo
.Protect
= PAGE_READONLY
;
2376 MemoryInfo
.RegionSize
= PAGE_SIZE
;
2380 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
2381 MemoryInfo
.State
= MEM_RESERVE
;
2382 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2383 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
2386 /* Return the data, NtQueryInformation already probed it*/
2387 if (PreviousMode
!= KernelMode
)
2391 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2392 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2396 Status
= _SEH2_GetExceptionCode();
2402 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2403 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2409 /* Check if this is for a local or remote process */
2410 if (ProcessHandle
== NtCurrentProcess())
2412 TargetProcess
= PsGetCurrentProcess();
2416 /* Reference the target process */
2417 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2418 PROCESS_QUERY_INFORMATION
,
2420 ExGetPreviousMode(),
2421 (PVOID
*)&TargetProcess
,
2423 if (!NT_SUCCESS(Status
)) return Status
;
2425 /* Attach to it now */
2426 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2430 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2431 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2433 /* Scan on the right */
2434 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2435 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2438 /* Check if this VAD covers the allocation range */
2439 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2440 (BaseVpn
<= Vad
->EndingVpn
))
2447 /* Check if this VAD is too high */
2448 if (BaseVpn
< Vad
->StartingVpn
)
2450 /* Stop if there is no left child */
2451 if (!Vad
->LeftChild
) break;
2453 /* Search on the left next */
2454 Vad
= Vad
->LeftChild
;
2458 /* Then this VAD is too low, keep searching on the right */
2459 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2461 /* Stop if there is no right child */
2462 if (!Vad
->RightChild
) break;
2464 /* Search on the right next */
2465 Vad
= Vad
->RightChild
;
2470 /* Was a VAD found? */
2473 Address
= PAGE_ALIGN(BaseAddress
);
2475 /* Calculate region size */
2478 if (Vad
->StartingVpn
>= BaseVpn
)
2480 /* Region size is the free space till the start of that VAD */
2481 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2485 /* Get the next VAD */
2486 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
2489 /* Region size is the free space till the start of that VAD */
2490 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2494 /* Maximum possible region size with that base address */
2495 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2501 /* Maximum possible region size with that base address */
2502 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2505 /* Check if we were attached */
2506 if (ProcessHandle
!= NtCurrentProcess())
2508 /* Detach and derefernece the process */
2509 KeUnstackDetachProcess(&ApcState
);
2510 ObDereferenceObject(TargetProcess
);
2513 /* Build the rest of the initial information block */
2514 MemoryInfo
.BaseAddress
= Address
;
2515 MemoryInfo
.AllocationBase
= NULL
;
2516 MemoryInfo
.AllocationProtect
= 0;
2517 MemoryInfo
.State
= MEM_FREE
;
2518 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2519 MemoryInfo
.Type
= 0;
2521 /* Return the data, NtQueryInformation already probed it*/
2522 if (PreviousMode
!= KernelMode
)
2526 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2527 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2531 Status
= _SEH2_GetExceptionCode();
2537 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2538 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2544 /* This must be a VM VAD */
2545 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2547 /* Lock the address space of the process */
2548 MmLockAddressSpace(&TargetProcess
->Vm
);
2550 /* Find the memory area the specified address belongs to */
2551 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
2552 ASSERT(MemoryArea
!= NULL
);
2554 /* Determine information dependent on the memory area type */
2555 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
2557 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
2558 ASSERT(NT_SUCCESS(Status
));
2562 /* Build the initial information block */
2563 Address
= PAGE_ALIGN(BaseAddress
);
2564 MemoryInfo
.BaseAddress
= Address
;
2565 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2566 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2567 MemoryInfo
.Type
= MEM_PRIVATE
;
2569 /* Find the largest chunk of memory which has the same state and protection mask */
2570 MemoryInfo
.State
= MiQueryAddressState(Address
,
2573 &MemoryInfo
.Protect
,
2575 Address
= NextAddress
;
2576 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2578 /* Keep going unless the state or protection mask changed */
2579 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2580 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2581 Address
= NextAddress
;
2584 /* Now that we know the last VA address, calculate the region size */
2585 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2588 /* Unlock the address space of the process */
2589 MmUnlockAddressSpace(&TargetProcess
->Vm
);
2591 /* Check if we were attached */
2592 if (ProcessHandle
!= NtCurrentProcess())
2594 /* Detach and derefernece the process */
2595 KeUnstackDetachProcess(&ApcState
);
2596 ObDereferenceObject(TargetProcess
);
2599 /* Return the data, NtQueryInformation already probed it*/
2600 if (PreviousMode
!= KernelMode
)
2604 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2605 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2607 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2609 Status
= _SEH2_GetExceptionCode();
2615 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2616 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2620 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2621 "State: %lx Type: %lx Size: %lx\n",
2622 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2623 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2624 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2631 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
2632 IN PVOID BaseAddress
,
2633 OUT PVOID MemoryInformation
,
2634 IN SIZE_T MemoryInformationLength
,
2635 OUT PSIZE_T ReturnLength
)
2639 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
2640 UNICODE_STRING ModuleFileName
;
2641 PMEMORY_SECTION_NAME SectionName
= NULL
;
2642 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2644 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2645 PROCESS_QUERY_INFORMATION
,
2651 if (!NT_SUCCESS(Status
))
2653 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
2657 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
2658 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
2660 if (NT_SUCCESS(Status
))
2662 SectionName
= MemoryInformation
;
2663 if (PreviousMode
!= KernelMode
)
2667 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2668 SectionName
->SectionFileName
.MaximumLength
= MemoryInformationLength
;
2669 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2671 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2674 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2676 Status
= _SEH2_GetExceptionCode();
2682 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2683 SectionName
->SectionFileName
.MaximumLength
= MemoryInformationLength
;
2684 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2686 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2690 ObDereferenceObject(Process
);
2696 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2697 IN PVOID BaseAddress
,
2698 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2699 OUT PVOID MemoryInformation
,
2700 IN SIZE_T MemoryInformationLength
,
2701 OUT PSIZE_T ReturnLength
)
2703 NTSTATUS Status
= STATUS_SUCCESS
;
2704 KPROCESSOR_MODE PreviousMode
;
2706 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2708 /* Bail out if the address is invalid */
2709 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2711 /* Probe return buffer */
2712 PreviousMode
= ExGetPreviousMode();
2713 if (PreviousMode
!= KernelMode
)
2717 ProbeForWrite(MemoryInformation
,
2718 MemoryInformationLength
,
2721 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
2723 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2725 Status
= _SEH2_GetExceptionCode();
2729 if (!NT_SUCCESS(Status
))
2735 switch(MemoryInformationClass
)
2737 case MemoryBasicInformation
:
2738 /* Validate the size information of the class */
2739 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2741 /* The size is invalid */
2742 return STATUS_INFO_LENGTH_MISMATCH
;
2744 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
2747 MemoryInformationLength
,
2751 case MemorySectionName
:
2752 /* Validate the size information of the class */
2753 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
2755 /* The size is invalid */
2756 return STATUS_INFO_LENGTH_MISMATCH
;
2758 Status
= MiQueryMemorySectionName(ProcessHandle
,
2761 MemoryInformationLength
,
2764 case MemoryWorkingSetList
:
2765 case MemoryBasicVlmInformation
:
2767 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);