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_COUNT ActualPages
= 0;
118 PETHREAD CurrentThread
= PsGetCurrentThread();
121 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
123 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
125 /* Lock the system working set */
126 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
131 /* Make sure there's some data about the page */
132 if (PointerPte
->u
.Long
)
134 /* As always, only handle current ARM3 scenarios */
135 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
136 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
138 /* Normally this is one possibility -- freeing a valid page */
139 if (PointerPte
->u
.Hard
.Valid
)
141 /* Get the page PFN */
142 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
143 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
145 /* Should not have any working set data yet */
146 ASSERT(Pfn1
->u1
.WsIndex
== 0);
148 /* Actual valid, legitimate, pages */
149 if (ValidPages
) (*ValidPages
)++;
151 /* Get the page table entry */
152 PageTableIndex
= Pfn1
->u4
.PteFrame
;
153 //Pfn2 = MiGetPfnEntry(PageTableIndex);
155 /* Lock the PFN database */
156 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
158 /* Delete it the page */
159 MI_SET_PFN_DELETED(Pfn1
);
160 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
162 /* Decrement the page table too */
163 DPRINT("FIXME: ARM3 should decrement the pool PDE refcount for: %p\n", PageTableIndex
);
164 #if 0 // ARM3: Dont't trust this yet
165 MiDecrementShareCount(Pfn2
, PageTableIndex
);
168 /* Release the PFN database */
169 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
171 /* Destroy the PTE */
172 PointerPte
->u
.Long
= 0;
175 /* Actual legitimate pages */
181 * The only other ARM3 possibility is a demand zero page, which would
182 * mean freeing some of the paged pool pages that haven't even been
183 * touched yet, as part of a larger allocation.
185 * Right now, we shouldn't expect any page file information in the PTE
187 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
189 /* Destroy the PTE */
190 PointerPte
->u
.Long
= 0;
198 /* Release the working set */
199 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
201 /* Flush the entire TLB */
202 KeFlushEntireTb(TRUE
, TRUE
);
210 MiDeletePte(IN PMMPTE PointerPte
,
211 IN PVOID VirtualAddress
,
212 IN PEPROCESS CurrentProcess
,
213 IN PMMPTE PrototypePte
)
217 PFN_NUMBER PageFrameIndex
;
220 /* PFN lock must be held */
221 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
223 /* Capture the PTE */
224 TempPte
= *PointerPte
;
226 /* We only support valid PTEs for now */
227 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
228 if (TempPte
.u
.Hard
.Valid
== 0)
230 /* Invalid PTEs not supported yet */
231 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
232 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
235 /* Get the PFN entry */
236 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
237 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
239 /* Check if this is a valid, prototype PTE */
240 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
242 /* Get the PDE and make sure it's faulted in */
243 PointerPde
= MiAddressToPde(PointerPte
);
244 if (PointerPde
->u
.Hard
.Valid
== 0)
246 #if (_MI_PAGING_LEVELS == 2)
247 /* Could be paged pool access from a new process -- synchronize the page directories */
248 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
251 /* The PDE must be valid at this point */
252 KeBugCheckEx(MEMORY_MANAGEMENT
,
254 (ULONG_PTR
)PointerPte
,
256 (ULONG_PTR
)VirtualAddress
);
258 #if (_MI_PAGING_LEVELS == 2)
261 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
262 //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PTE(PointerPde));
264 /* Drop the share count */
265 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
268 if (PointerPte
<= MiHighestUserPte
) ASSERT(PrototypePte
== Pfn1
->PteAddress
);
272 /* Make sure the saved PTE address is valid */
273 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
275 /* The PFN entry is illegal, or invalid */
276 KeBugCheckEx(MEMORY_MANAGEMENT
,
278 (ULONG_PTR
)PointerPte
,
280 (ULONG_PTR
)Pfn1
->PteAddress
);
283 /* There should only be 1 shared reference count */
284 ASSERT(Pfn1
->u2
.ShareCount
== 1);
286 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
287 //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
289 /* Mark the PFN for deletion and dereference what should be the last ref */
290 MI_SET_PFN_DELETED(Pfn1
);
291 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
293 /* We should eventually do this */
294 //CurrentProcess->NumberOfPrivatePages--;
297 /* Destroy the PTE and flush the TLB */
298 PointerPte
->u
.Long
= 0;
304 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
305 IN ULONG_PTR EndingAddress
,
308 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
311 PEPROCESS CurrentProcess
;
313 BOOLEAN AddressGap
= FALSE
;
314 PSUBSECTION Subsection
;
316 /* Get out if this is a fake VAD, RosMm will free the marea pages */
317 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
319 /* Grab the process and PTE/PDE for the address being deleted */
320 CurrentProcess
= PsGetCurrentProcess();
321 PointerPde
= MiAddressToPde(Va
);
322 PointerPte
= MiAddressToPte(Va
);
324 /* Check if this is a section VAD or a VM VAD */
325 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
327 /* Don't worry about prototypes */
328 PrototypePte
= LastPrototypePte
= NULL
;
332 /* Get the prototype PTE */
333 PrototypePte
= Vad
->FirstPrototypePte
;
334 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
337 /* In all cases, we don't support fork() yet */
338 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
340 /* Loop the PTE for each VA */
343 /* First keep going until we find a valid PDE */
344 while (!PointerPde
->u
.Long
)
346 /* There are gaps in the address space */
349 /* Still no valid PDE, try the next 4MB (or whatever) */
352 /* Update the PTE on this new boundary */
353 PointerPte
= MiPteToAddress(PointerPde
);
355 /* Check if all the PDEs are invalid, so there's nothing to free */
356 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
357 if (Va
> EndingAddress
) return;
360 /* Now check if the PDE is mapped in */
361 if (!PointerPde
->u
.Hard
.Valid
)
363 /* It isn't, so map it in */
364 PointerPte
= MiPteToAddress(PointerPde
);
365 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
368 /* Now we should have a valid PDE, mapped in, and still have some VA */
369 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
370 ASSERT(Va
<= EndingAddress
);
372 /* Check if this is a section VAD with gaps in it */
373 if ((AddressGap
) && (LastPrototypePte
))
375 /* We need to skip to the next correct prototype PTE */
376 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
378 /* And we need the subsection to skip to the next last prototype PTE */
379 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
383 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
387 /* No more subsections, we are done with prototype PTEs */
392 /* Lock the PFN Database while we delete the PTEs */
393 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
396 /* Capture the PDE and make sure it exists */
397 TempPte
= *PointerPte
;
400 /* Check if the PTE is actually mapped in */
401 if (TempPte
.u
.Long
& 0xFFFFFC01)
403 /* Are we dealing with section VAD? */
404 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
406 /* We need to skip to the next correct prototype PTE */
407 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
409 /* And we need the subsection to skip to the next last prototype PTE */
410 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
414 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
418 /* No more subsections, we are done with prototype PTEs */
423 /* Check for prototype PTE */
424 if ((TempPte
.u
.Hard
.Valid
== 0) &&
425 (TempPte
.u
.Soft
.Prototype
== 1))
428 PointerPte
->u
.Long
= 0;
432 /* Delete the PTE proper */
433 MiDeletePte(PointerPte
,
441 /* The PTE was never mapped, just nuke it here */
442 PointerPte
->u
.Long
= 0;
446 /* Update the address and PTE for it */
451 /* Making sure the PDE is still valid */
452 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
454 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
456 /* The PDE should still be valid at this point */
457 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
459 /* Release the lock and get out if we're done */
460 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
461 if (Va
> EndingAddress
) return;
463 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
464 PointerPde
= MiAddressToPde(Va
);
470 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
471 OUT PBOOLEAN HaveBadAddress
,
472 OUT PULONG_PTR BadAddress
)
474 PEXCEPTION_RECORD ExceptionRecord
;
480 *HaveBadAddress
= FALSE
;
483 // Get the exception record
485 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
488 // Look at the exception code
490 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
491 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
492 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
495 // We can tell the address if we have more than one parameter
497 if (ExceptionRecord
->NumberParameters
> 1)
500 // Return the address
502 *HaveBadAddress
= TRUE
;
503 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
508 // Continue executing the next handler
510 return EXCEPTION_EXECUTE_HANDLER
;
515 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
516 IN PVOID SourceAddress
,
517 IN PEPROCESS TargetProcess
,
518 OUT PVOID TargetAddress
,
519 IN SIZE_T BufferSize
,
520 IN KPROCESSOR_MODE PreviousMode
,
521 OUT PSIZE_T ReturnSize
)
523 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
524 PMDL Mdl
= (PMDL
)MdlBuffer
;
525 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
526 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
527 volatile BOOLEAN PagesLocked
;
528 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
529 volatile PVOID MdlAddress
;
531 BOOLEAN HaveBadAddress
;
532 ULONG_PTR BadAddress
;
533 NTSTATUS Status
= STATUS_SUCCESS
;
537 // Calculate the maximum amount of data to move
539 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
540 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
541 CurrentSize
= TotalSize
;
542 RemainingSize
= BufferSize
;
545 // Loop as long as there is still data
547 while (RemainingSize
> 0)
550 // Check if this transfer will finish everything off
552 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
555 // Attach to the source address space
557 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
560 // Reset state for this pass
564 FailedInMoving
= FALSE
;
565 ASSERT(FailedInProbe
== FALSE
);
568 // Protect user-mode copy
573 // If this is our first time, probe the buffer
575 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
578 // Catch a failure here
580 FailedInProbe
= TRUE
;
585 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
590 FailedInProbe
= FALSE
;
594 // Initialize and probe and lock the MDL
596 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
597 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
603 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
612 // Use our SEH handler to pick this up
614 FailedInMapping
= TRUE
;
615 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
619 // Now let go of the source and grab to the target process
621 KeUnstackDetachProcess(&ApcState
);
622 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
625 // Check if this is our first time through
627 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
630 // Catch a failure here
632 FailedInProbe
= TRUE
;
637 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
642 FailedInProbe
= FALSE
;
646 // Now do the actual move
648 FailedInMoving
= TRUE
;
649 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
651 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
656 // Detach from whoever we may be attached to
658 KeUnstackDetachProcess(&ApcState
);
661 // Check if we had mapped the pages
663 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
666 // Check if we had locked the pages
668 if (PagesLocked
) MmUnlockPages(Mdl
);
671 // Check if we hit working set quota
673 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
678 return STATUS_WORKING_SET_QUOTA
;
682 // Check if we failed during the probe or mapping
684 if ((FailedInProbe
) || (FailedInMapping
))
689 Status
= _SEH2_GetExceptionCode();
690 _SEH2_YIELD(return Status
);
694 // Otherwise, we failed probably during the move
696 *ReturnSize
= BufferSize
- RemainingSize
;
700 // Check if we know exactly where we stopped copying
705 // Return the exact number of bytes copied
707 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
712 // Return partial copy
714 Status
= STATUS_PARTIAL_COPY
;
719 // Check for SEH status
721 if (Status
!= STATUS_SUCCESS
) return Status
;
724 // Detach from target
726 KeUnstackDetachProcess(&ApcState
);
731 MmUnmapLockedPages(MdlAddress
, Mdl
);
735 // Update location and size
737 RemainingSize
-= CurrentSize
;
738 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
739 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
745 *ReturnSize
= BufferSize
;
746 return STATUS_SUCCESS
;
751 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
752 IN PVOID SourceAddress
,
753 IN PEPROCESS TargetProcess
,
754 OUT PVOID TargetAddress
,
755 IN SIZE_T BufferSize
,
756 IN KPROCESSOR_MODE PreviousMode
,
757 OUT PSIZE_T ReturnSize
)
759 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
760 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
761 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
762 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
765 BOOLEAN HaveBadAddress
;
766 ULONG_PTR BadAddress
;
767 NTSTATUS Status
= STATUS_SUCCESS
;
771 // Calculate the maximum amount of data to move
773 TotalSize
= MI_MAX_TRANSFER_SIZE
;
774 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
775 CurrentSize
= TotalSize
;
776 RemainingSize
= BufferSize
;
779 // Check if we can use the stack
781 if (BufferSize
<= MI_POOL_COPY_BYTES
)
786 PoolAddress
= (PVOID
)StackBuffer
;
793 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
794 if (!PoolAddress
) ASSERT(FALSE
);
795 HavePoolAddress
= TRUE
;
799 // Loop as long as there is still data
801 while (RemainingSize
> 0)
804 // Check if this transfer will finish everything off
806 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
809 // Attach to the source address space
811 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
814 // Reset state for this pass
816 FailedInMoving
= FALSE
;
817 ASSERT(FailedInProbe
== FALSE
);
820 // Protect user-mode copy
825 // If this is our first time, probe the buffer
827 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
830 // Catch a failure here
832 FailedInProbe
= TRUE
;
837 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
842 FailedInProbe
= FALSE
;
848 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
851 // Now let go of the source and grab to the target process
853 KeUnstackDetachProcess(&ApcState
);
854 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
857 // Check if this is our first time through
859 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
862 // Catch a failure here
864 FailedInProbe
= TRUE
;
869 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
874 FailedInProbe
= FALSE
;
878 // Now do the actual move
880 FailedInMoving
= TRUE
;
881 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
883 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
888 // Detach from whoever we may be attached to
890 KeUnstackDetachProcess(&ApcState
);
893 // Check if we had allocated pool
895 if (HavePoolAddress
) ExFreePool(PoolAddress
);
898 // Check if we failed during the probe
905 Status
= _SEH2_GetExceptionCode();
906 _SEH2_YIELD(return Status
);
910 // Otherwise, we failed, probably during the move
912 *ReturnSize
= BufferSize
- RemainingSize
;
916 // Check if we know exactly where we stopped copying
921 // Return the exact number of bytes copied
923 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
928 // Return partial copy
930 Status
= STATUS_PARTIAL_COPY
;
935 // Check for SEH status
937 if (Status
!= STATUS_SUCCESS
) return Status
;
940 // Detach from target
942 KeUnstackDetachProcess(&ApcState
);
945 // Update location and size
947 RemainingSize
-= CurrentSize
;
948 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
949 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
954 // Check if we had allocated pool
956 if (HavePoolAddress
) ExFreePool(PoolAddress
);
961 *ReturnSize
= BufferSize
;
962 return STATUS_SUCCESS
;
967 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
968 IN PVOID SourceAddress
,
969 IN PEPROCESS TargetProcess
,
970 OUT PVOID TargetAddress
,
971 IN SIZE_T BufferSize
,
972 IN KPROCESSOR_MODE PreviousMode
,
973 OUT PSIZE_T ReturnSize
)
976 PEPROCESS Process
= SourceProcess
;
979 // Don't accept zero-sized buffers
981 if (!BufferSize
) return STATUS_SUCCESS
;
984 // If we are copying from ourselves, lock the target instead
986 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
989 // Acquire rundown protection
991 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
996 return STATUS_PROCESS_IS_TERMINATING
;
1000 // See if we should use the pool copy
1002 if (BufferSize
> MI_POOL_COPY_BYTES
)
1007 Status
= MiDoMappedCopy(SourceProcess
,
1020 Status
= MiDoPoolCopy(SourceProcess
,
1032 ExReleaseRundownProtection(&Process
->RundownProtect
);
1038 MmFlushVirtualMemory(IN PEPROCESS Process
,
1039 IN OUT PVOID
*BaseAddress
,
1040 IN OUT PSIZE_T RegionSize
,
1041 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1049 return STATUS_SUCCESS
;
1054 MiGetPageProtection(IN PMMPTE PointerPte
)
1060 /* Copy this PTE's contents */
1061 TempPte
= *PointerPte
;
1063 /* Assure it's not totally zero */
1064 ASSERT(TempPte
.u
.Long
);
1066 /* Check for a special prototype format */
1067 if (TempPte
.u
.Soft
.Valid
== 0 &&
1068 TempPte
.u
.Soft
.Prototype
== 1)
1070 /* Unsupported now */
1075 /* In the easy case of transition or demand zero PTE just return its protection */
1076 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1078 /* If we get here, the PTE is valid, so look up the page in PFN database */
1079 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1081 if (!Pfn
->u3
.e1
.PrototypePte
)
1083 /* Return protection of the original pte */
1084 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1087 /* This is hardware PTE */
1091 return PAGE_NOACCESS
;
1096 MiQueryAddressState(IN PVOID Va
,
1098 IN PEPROCESS TargetProcess
,
1099 OUT PULONG ReturnedProtect
,
1106 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1107 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1108 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1109 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1111 /* Only normal VADs supported */
1112 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1114 /* Get the PDE and PTE for the address */
1115 PointerPde
= MiAddressToPde(Va
);
1116 PointerPte
= MiAddressToPte(Va
);
1118 /* Return the next range */
1119 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1121 /* Loop to make sure the PDE is valid */
1127 /* Is the PDE empty? */
1128 if (!PointerPde
->u
.Long
)
1130 /* No address in this range used yet, move to the next PDE range */
1131 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1135 /* The PDE is not empty, but is it faulted in? */
1136 if (!PointerPde
->u
.Hard
.Valid
)
1138 /* It isn't, go ahead and do the fault */
1139 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1143 /* Check if the PDE was faulted in, making the PTE readable */
1144 if (!LockChange
) ValidPte
= TRUE
;
1145 } while (LockChange
);
1147 /* Is it safe to try reading the PTE? */
1150 /* FIXME: watch out for large pages */
1152 /* Capture the PTE */
1153 TempPte
= *PointerPte
;
1156 /* The PTE is valid, so it's not zeroed out */
1157 DemandZeroPte
= FALSE
;
1159 /* Check if it's valid or has a valid protection mask */
1160 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1161 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1162 (TempPte
.u
.Hard
.Valid
== 1))
1164 /* This means it's committed */
1167 /* Get protection state of this page */
1168 Protect
= MiGetPageProtection(PointerPte
);
1172 /* Otherwise our defaults should hold */
1173 ASSERT(Protect
== 0);
1174 ASSERT(State
== MEM_RESERVE
);
1179 /* Check if this was a demand-zero PTE, since we need to find the state */
1182 /* Check if the VAD is for committed memory */
1183 if (Vad
->u
.VadFlags
.MemCommit
)
1185 /* This is committed memory */
1188 /* Convert the protection */
1189 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1193 /* Return the protection code */
1194 *ReturnedProtect
= Protect
;
1198 /* PUBLIC FUNCTIONS ***********************************************************/
1205 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1216 MmSecureVirtualMemory(IN PVOID Address
,
1220 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1229 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1231 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1234 /* SYSTEM CALLS ***************************************************************/
1238 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1239 IN PVOID BaseAddress
,
1241 IN SIZE_T NumberOfBytesToRead
,
1242 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1244 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1246 NTSTATUS Status
= STATUS_SUCCESS
;
1247 SIZE_T BytesRead
= 0;
1251 // Check if we came from user mode
1253 if (PreviousMode
!= KernelMode
)
1256 // Validate the read addresses
1258 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1259 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1260 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1261 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1264 // Don't allow to write into kernel space
1266 return STATUS_ACCESS_VIOLATION
;
1270 // Enter SEH for probe
1275 // Probe the output value
1277 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1279 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1282 // Get exception code
1284 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1290 // Don't do zero-byte transfers
1292 if (NumberOfBytesToRead
)
1295 // Reference the process
1297 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1303 if (NT_SUCCESS(Status
))
1308 Status
= MmCopyVirtualMemory(Process
,
1310 PsGetCurrentProcess(),
1312 NumberOfBytesToRead
,
1317 // Dereference the process
1319 ObDereferenceObject(Process
);
1324 // Check if the caller sent this parameter
1326 if (NumberOfBytesRead
)
1329 // Enter SEH to guard write
1334 // Return the number of bytes read
1336 *NumberOfBytesRead
= BytesRead
;
1338 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1352 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1353 IN PVOID BaseAddress
,
1355 IN SIZE_T NumberOfBytesToWrite
,
1356 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1358 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1360 NTSTATUS Status
= STATUS_SUCCESS
;
1361 SIZE_T BytesWritten
= 0;
1365 // Check if we came from user mode
1367 if (PreviousMode
!= KernelMode
)
1370 // Validate the read addresses
1372 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1373 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1374 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1375 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1378 // Don't allow to write into kernel space
1380 return STATUS_ACCESS_VIOLATION
;
1384 // Enter SEH for probe
1389 // Probe the output value
1391 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1396 // Get exception code
1398 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1404 // Don't do zero-byte transfers
1406 if (NumberOfBytesToWrite
)
1409 // Reference the process
1411 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1417 if (NT_SUCCESS(Status
))
1422 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1426 NumberOfBytesToWrite
,
1431 // Dereference the process
1433 ObDereferenceObject(Process
);
1438 // Check if the caller sent this parameter
1440 if (NumberOfBytesWritten
)
1443 // Enter SEH to guard write
1448 // Return the number of bytes written
1450 *NumberOfBytesWritten
= BytesWritten
;
1452 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1466 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1467 IN OUT PVOID
*UnsafeBaseAddress
,
1468 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1469 IN ULONG NewAccessProtection
,
1470 OUT PULONG UnsafeOldAccessProtection
)
1473 ULONG OldAccessProtection
;
1475 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1476 PVOID BaseAddress
= NULL
;
1477 SIZE_T NumberOfBytesToProtect
= 0;
1478 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1480 BOOLEAN Attached
= FALSE
;
1481 KAPC_STATE ApcState
;
1485 // Check for valid protection flags
1487 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1488 if (Protection
!= PAGE_NOACCESS
&&
1489 Protection
!= PAGE_READONLY
&&
1490 Protection
!= PAGE_READWRITE
&&
1491 Protection
!= PAGE_WRITECOPY
&&
1492 Protection
!= PAGE_EXECUTE
&&
1493 Protection
!= PAGE_EXECUTE_READ
&&
1494 Protection
!= PAGE_EXECUTE_READWRITE
&&
1495 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1500 return STATUS_INVALID_PAGE_PROTECTION
;
1504 // Check if we came from user mode
1506 if (PreviousMode
!= KernelMode
)
1509 // Enter SEH for probing
1514 // Validate all outputs
1516 ProbeForWritePointer(UnsafeBaseAddress
);
1517 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1518 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1523 BaseAddress
= *UnsafeBaseAddress
;
1524 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1526 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1529 // Get exception code
1531 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1540 BaseAddress
= *UnsafeBaseAddress
;
1541 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1545 // Catch illegal base address
1547 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1550 // Catch illegal region size
1552 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1557 return STATUS_INVALID_PARAMETER_3
;
1561 // 0 is also illegal
1563 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1566 // Get a reference to the process
1568 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1569 PROCESS_VM_OPERATION
,
1574 if (!NT_SUCCESS(Status
)) return Status
;
1577 // Check if we should attach
1579 if (CurrentProcess
!= Process
)
1584 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1589 // Do the actual work
1591 Status
= MiProtectVirtualMemory(Process
,
1593 &NumberOfBytesToProtect
,
1594 NewAccessProtection
,
1595 &OldAccessProtection
);
1600 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1603 // Release reference
1605 ObDereferenceObject(Process
);
1608 // Enter SEH to return data
1613 // Return data to user
1615 *UnsafeOldAccessProtection
= OldAccessProtection
;
1616 *UnsafeBaseAddress
= BaseAddress
;
1617 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1619 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1632 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1633 IN OUT PVOID
*BaseAddress
,
1634 IN OUT PSIZE_T NumberOfBytesToLock
,
1638 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1640 BOOLEAN Attached
= FALSE
;
1641 KAPC_STATE ApcState
;
1642 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1643 PVOID CapturedBaseAddress
;
1644 SIZE_T CapturedBytesToLock
;
1650 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1653 // Invalid set of flags
1655 return STATUS_INVALID_PARAMETER
;
1659 // At least one flag must be specified
1661 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1666 return STATUS_INVALID_PARAMETER
;
1670 // Enter SEH for probing
1675 // Validate output data
1677 ProbeForWritePointer(BaseAddress
);
1678 ProbeForWriteSize_t(NumberOfBytesToLock
);
1683 CapturedBaseAddress
= *BaseAddress
;
1684 CapturedBytesToLock
= *NumberOfBytesToLock
;
1686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1689 // Get exception code
1691 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1696 // Catch illegal base address
1698 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1701 // Catch illegal region size
1703 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1708 return STATUS_INVALID_PARAMETER
;
1712 // 0 is also illegal
1714 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1717 // Get a reference to the process
1719 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1720 PROCESS_VM_OPERATION
,
1725 if (!NT_SUCCESS(Status
)) return Status
;
1728 // Check if this is is system-mapped
1730 if (MapType
& MAP_SYSTEM
)
1733 // Check for required privilege
1735 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1738 // Fail: Don't have it
1740 ObDereferenceObject(Process
);
1741 return STATUS_PRIVILEGE_NOT_HELD
;
1746 // Check if we should attach
1748 if (CurrentProcess
!= Process
)
1753 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1765 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1768 // Release reference
1770 ObDereferenceObject(Process
);
1773 // Enter SEH to return data
1778 // Return data to user
1780 *BaseAddress
= CapturedBaseAddress
;
1781 *NumberOfBytesToLock
= 0;
1783 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1786 // Get exception code
1788 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1795 return STATUS_SUCCESS
;
1800 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1801 IN OUT PVOID
*BaseAddress
,
1802 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1806 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1808 BOOLEAN Attached
= FALSE
;
1809 KAPC_STATE ApcState
;
1810 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1811 PVOID CapturedBaseAddress
;
1812 SIZE_T CapturedBytesToUnlock
;
1818 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1821 // Invalid set of flags
1823 return STATUS_INVALID_PARAMETER
;
1827 // At least one flag must be specified
1829 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1834 return STATUS_INVALID_PARAMETER
;
1838 // Enter SEH for probing
1843 // Validate output data
1845 ProbeForWritePointer(BaseAddress
);
1846 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1851 CapturedBaseAddress
= *BaseAddress
;
1852 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1854 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1857 // Get exception code
1859 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1864 // Catch illegal base address
1866 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1869 // Catch illegal region size
1871 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1876 return STATUS_INVALID_PARAMETER
;
1880 // 0 is also illegal
1882 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1885 // Get a reference to the process
1887 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1888 PROCESS_VM_OPERATION
,
1893 if (!NT_SUCCESS(Status
)) return Status
;
1896 // Check if this is is system-mapped
1898 if (MapType
& MAP_SYSTEM
)
1901 // Check for required privilege
1903 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1906 // Fail: Don't have it
1908 ObDereferenceObject(Process
);
1909 return STATUS_PRIVILEGE_NOT_HELD
;
1914 // Check if we should attach
1916 if (CurrentProcess
!= Process
)
1921 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1933 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1936 // Release reference
1938 ObDereferenceObject(Process
);
1941 // Enter SEH to return data
1946 // Return data to user
1948 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1949 *NumberOfBytesToUnlock
= 0;
1951 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1954 // Get exception code
1956 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1963 return STATUS_SUCCESS
;
1968 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1969 IN OUT PVOID
*BaseAddress
,
1970 IN OUT PSIZE_T NumberOfBytesToFlush
,
1971 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1975 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1976 PVOID CapturedBaseAddress
;
1977 SIZE_T CapturedBytesToFlush
;
1978 IO_STATUS_BLOCK LocalStatusBlock
;
1982 // Check if we came from user mode
1984 if (PreviousMode
!= KernelMode
)
1987 // Enter SEH for probing
1992 // Validate all outputs
1994 ProbeForWritePointer(BaseAddress
);
1995 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1996 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2001 CapturedBaseAddress
= *BaseAddress
;
2002 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2004 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2007 // Get exception code
2009 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2018 CapturedBaseAddress
= *BaseAddress
;
2019 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2023 // Catch illegal base address
2025 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2028 // Catch illegal region size
2030 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2035 return STATUS_INVALID_PARAMETER
;
2039 // Get a reference to the process
2041 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2042 PROCESS_VM_OPERATION
,
2047 if (!NT_SUCCESS(Status
)) return Status
;
2052 Status
= MmFlushVirtualMemory(Process
,
2053 &CapturedBaseAddress
,
2054 &CapturedBytesToFlush
,
2058 // Release reference
2060 ObDereferenceObject(Process
);
2063 // Enter SEH to return data
2068 // Return data to user
2070 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2071 *NumberOfBytesToFlush
= 0;
2072 *IoStatusBlock
= LocalStatusBlock
;
2074 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2090 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2092 IN PVOID BaseAddress
,
2093 IN SIZE_T RegionSize
,
2094 IN PVOID
*UserAddressArray
,
2095 OUT PULONG_PTR EntriesInUserAddressArray
,
2096 OUT PULONG Granularity
)
2101 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2102 ULONG_PTR CapturedEntryCount
;
2106 // Check if we came from user mode
2108 if (PreviousMode
!= KernelMode
)
2111 // Enter SEH for probing
2116 // Catch illegal base address
2118 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2121 // Catch illegal region size
2123 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2128 return STATUS_INVALID_PARAMETER_3
;
2132 // Validate all data
2134 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2135 ProbeForWriteUlong(Granularity
);
2140 CapturedEntryCount
= *EntriesInUserAddressArray
;
2143 // Must have a count
2145 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2148 // Can't be larger than the maximum
2150 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2155 return STATUS_INVALID_PARAMETER_5
;
2159 // Probe the actual array
2161 ProbeForWrite(UserAddressArray
,
2162 CapturedEntryCount
* sizeof(PVOID
),
2165 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2168 // Get exception code
2170 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2179 CapturedEntryCount
= *EntriesInUserAddressArray
;
2180 ASSERT(CapturedEntryCount
!= 0);
2184 // Check if this is a local request
2186 if (ProcessHandle
== NtCurrentProcess())
2189 // No need to reference the process
2191 Process
= PsGetCurrentProcess();
2196 // Reference the target
2198 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2199 PROCESS_VM_OPERATION
,
2204 if (!NT_SUCCESS(Status
)) return Status
;
2208 // Compute the last address and validate it
2210 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2211 if (BaseAddress
> EndAddress
)
2216 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2217 return STATUS_INVALID_PARAMETER_4
;
2226 // Dereference if needed
2228 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2231 // Enter SEH to return data
2236 // Return data to user
2238 *EntriesInUserAddressArray
= 0;
2239 *Granularity
= PAGE_SIZE
;
2241 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2244 // Get exception code
2246 Status
= _SEH2_GetExceptionCode();
2253 return STATUS_SUCCESS
;
2261 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2262 IN PVOID BaseAddress
,
2263 IN SIZE_T RegionSize
)
2268 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2269 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2272 // Catch illegal base address
2274 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2277 // Catch illegal region size
2279 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2284 return STATUS_INVALID_PARAMETER_3
;
2288 // Check if this is a local request
2290 if (ProcessHandle
== NtCurrentProcess())
2293 // No need to reference the process
2295 Process
= PsGetCurrentProcess();
2300 // Reference the target
2302 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2303 PROCESS_VM_OPERATION
,
2308 if (!NT_SUCCESS(Status
)) return Status
;
2312 // Compute the last address and validate it
2314 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2315 if (BaseAddress
> EndAddress
)
2320 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2321 return STATUS_INVALID_PARAMETER_3
;
2330 // Dereference if needed
2332 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2337 return STATUS_SUCCESS
;
2342 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
2343 IN PVOID BaseAddress
,
2344 OUT PVOID MemoryInformation
,
2345 IN SIZE_T MemoryInformationLength
,
2346 OUT PSIZE_T ReturnLength
)
2348 PEPROCESS TargetProcess
;
2349 NTSTATUS Status
= STATUS_SUCCESS
;
2351 PVOID Address
, NextAddress
;
2352 BOOLEAN Found
= FALSE
;
2353 ULONG NewProtect
, NewState
;
2355 MEMORY_BASIC_INFORMATION MemoryInfo
;
2356 KAPC_STATE ApcState
;
2357 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2358 PMEMORY_AREA MemoryArea
;
2359 SIZE_T ResultLength
;
2361 /* Check for illegal addresses in user-space, or the shared memory area */
2362 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2363 (PAGE_ALIGN(BaseAddress
) == (PVOID
)USER_SHARED_DATA
))
2365 Address
= PAGE_ALIGN(BaseAddress
);
2367 /* Make up an info structure describing this range */
2368 MemoryInfo
.BaseAddress
= Address
;
2369 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
2370 MemoryInfo
.Type
= MEM_PRIVATE
;
2372 /* Special case for shared data */
2373 if (Address
== (PVOID
)USER_SHARED_DATA
)
2375 MemoryInfo
.AllocationBase
= (PVOID
)USER_SHARED_DATA
;
2376 MemoryInfo
.State
= MEM_COMMIT
;
2377 MemoryInfo
.Protect
= PAGE_READONLY
;
2378 MemoryInfo
.RegionSize
= PAGE_SIZE
;
2382 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
2383 MemoryInfo
.State
= MEM_RESERVE
;
2384 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2385 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
2388 /* Return the data, NtQueryInformation already probed it*/
2389 if (PreviousMode
!= KernelMode
)
2393 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2394 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2396 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2398 Status
= _SEH2_GetExceptionCode();
2404 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2405 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2411 /* Check if this is for a local or remote process */
2412 if (ProcessHandle
== NtCurrentProcess())
2414 TargetProcess
= PsGetCurrentProcess();
2418 /* Reference the target process */
2419 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2420 PROCESS_QUERY_INFORMATION
,
2422 ExGetPreviousMode(),
2423 (PVOID
*)&TargetProcess
,
2425 if (!NT_SUCCESS(Status
)) return Status
;
2427 /* Attach to it now */
2428 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2432 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2433 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2435 /* Scan on the right */
2436 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2437 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2440 /* Check if this VAD covers the allocation range */
2441 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2442 (BaseVpn
<= Vad
->EndingVpn
))
2449 /* Check if this VAD is too high */
2450 if (BaseVpn
< Vad
->StartingVpn
)
2452 /* Stop if there is no left child */
2453 if (!Vad
->LeftChild
) break;
2455 /* Search on the left next */
2456 Vad
= Vad
->LeftChild
;
2460 /* Then this VAD is too low, keep searching on the right */
2461 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2463 /* Stop if there is no right child */
2464 if (!Vad
->RightChild
) break;
2466 /* Search on the right next */
2467 Vad
= Vad
->RightChild
;
2472 /* Was a VAD found? */
2475 Address
= PAGE_ALIGN(BaseAddress
);
2477 /* Calculate region size */
2480 if (Vad
->StartingVpn
>= BaseVpn
)
2482 /* Region size is the free space till the start of that VAD */
2483 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2487 /* Get the next VAD */
2488 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
2491 /* Region size is the free space till the start of that VAD */
2492 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2496 /* Maximum possible region size with that base address */
2497 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2503 /* Maximum possible region size with that base address */
2504 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2507 /* Check if we were attached */
2508 if (ProcessHandle
!= NtCurrentProcess())
2510 /* Detach and derefernece the process */
2511 KeUnstackDetachProcess(&ApcState
);
2512 ObDereferenceObject(TargetProcess
);
2515 /* Build the rest of the initial information block */
2516 MemoryInfo
.BaseAddress
= Address
;
2517 MemoryInfo
.AllocationBase
= NULL
;
2518 MemoryInfo
.AllocationProtect
= 0;
2519 MemoryInfo
.State
= MEM_FREE
;
2520 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2521 MemoryInfo
.Type
= 0;
2523 /* Return the data, NtQueryInformation already probed it*/
2524 if (PreviousMode
!= KernelMode
)
2528 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2529 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2531 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2533 Status
= _SEH2_GetExceptionCode();
2539 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2540 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2546 /* This must be a VM VAD */
2547 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2549 /* Lock the address space of the process */
2550 MmLockAddressSpace(&TargetProcess
->Vm
);
2552 /* Find the memory area the specified address belongs to */
2553 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
2554 ASSERT(MemoryArea
!= NULL
);
2556 /* Determine information dependent on the memory area type */
2557 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
2559 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
2560 ASSERT(NT_SUCCESS(Status
));
2564 /* Build the initial information block */
2565 Address
= PAGE_ALIGN(BaseAddress
);
2566 MemoryInfo
.BaseAddress
= Address
;
2567 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2568 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2569 MemoryInfo
.Type
= MEM_PRIVATE
;
2571 /* Find the largest chunk of memory which has the same state and protection mask */
2572 MemoryInfo
.State
= MiQueryAddressState(Address
,
2575 &MemoryInfo
.Protect
,
2577 Address
= NextAddress
;
2578 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2580 /* Keep going unless the state or protection mask changed */
2581 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2582 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2583 Address
= NextAddress
;
2586 /* Now that we know the last VA address, calculate the region size */
2587 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2590 /* Unlock the address space of the process */
2591 MmUnlockAddressSpace(&TargetProcess
->Vm
);
2593 /* Check if we were attached */
2594 if (ProcessHandle
!= NtCurrentProcess())
2596 /* Detach and derefernece the process */
2597 KeUnstackDetachProcess(&ApcState
);
2598 ObDereferenceObject(TargetProcess
);
2601 /* Return the data, NtQueryInformation already probed it*/
2602 if (PreviousMode
!= KernelMode
)
2606 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2607 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2609 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2611 Status
= _SEH2_GetExceptionCode();
2617 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2618 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2622 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2623 "State: %lx Type: %lx Size: %lx\n",
2624 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2625 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2626 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2633 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
2634 IN PVOID BaseAddress
,
2635 OUT PVOID MemoryInformation
,
2636 IN SIZE_T MemoryInformationLength
,
2637 OUT PSIZE_T ReturnLength
)
2641 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
2642 UNICODE_STRING ModuleFileName
;
2643 PMEMORY_SECTION_NAME SectionName
= NULL
;
2644 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2646 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2647 PROCESS_QUERY_INFORMATION
,
2653 if (!NT_SUCCESS(Status
))
2655 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
2659 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
2660 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
2662 if (NT_SUCCESS(Status
))
2664 SectionName
= MemoryInformation
;
2665 if (PreviousMode
!= KernelMode
)
2669 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2670 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2671 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2673 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2676 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2678 Status
= _SEH2_GetExceptionCode();
2684 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2685 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2686 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2688 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2692 ObDereferenceObject(Process
);
2698 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2699 IN PVOID BaseAddress
,
2700 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2701 OUT PVOID MemoryInformation
,
2702 IN SIZE_T MemoryInformationLength
,
2703 OUT PSIZE_T ReturnLength
)
2705 NTSTATUS Status
= STATUS_SUCCESS
;
2706 KPROCESSOR_MODE PreviousMode
;
2708 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2710 /* Bail out if the address is invalid */
2711 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2713 /* Probe return buffer */
2714 PreviousMode
= ExGetPreviousMode();
2715 if (PreviousMode
!= KernelMode
)
2719 ProbeForWrite(MemoryInformation
,
2720 MemoryInformationLength
,
2723 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
2725 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2727 Status
= _SEH2_GetExceptionCode();
2731 if (!NT_SUCCESS(Status
))
2737 switch(MemoryInformationClass
)
2739 case MemoryBasicInformation
:
2740 /* Validate the size information of the class */
2741 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2743 /* The size is invalid */
2744 return STATUS_INFO_LENGTH_MISMATCH
;
2746 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
2749 MemoryInformationLength
,
2753 case MemorySectionName
:
2754 /* Validate the size information of the class */
2755 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
2757 /* The size is invalid */
2758 return STATUS_INFO_LENGTH_MISMATCH
;
2760 Status
= MiQueryMemorySectionName(ProcessHandle
,
2763 MemoryInformationLength
,
2766 case MemoryWorkingSetList
:
2767 case MemoryBasicVlmInformation
:
2769 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);