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 #line 15 "ARMĀ³::VIRTUAL"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
30 /* PRIVATE FUNCTIONS **********************************************************/
34 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
35 IN PEPROCESS CurrentProcess
)
38 BOOLEAN LockChange
= FALSE
;
40 /* Must be a non-pool page table, since those are double-mapped already */
41 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
42 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
43 (PageTableVirtualAddress
> MmPagedPoolEnd
));
45 /* Working set lock or PFN lock should be held */
46 ASSERT(KeAreAllApcsDisabled() == TRUE
);
48 /* Check if the page table is valid */
49 while (!MmIsAddressValid(PageTableVirtualAddress
))
52 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
53 if (!NT_SUCCESS(Status
))
55 /* This should not fail */
56 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
59 (ULONG_PTR
)CurrentProcess
,
60 (ULONG_PTR
)PageTableVirtualAddress
);
63 /* This flag will be useful later when we do better locking */
67 /* Let caller know what the lock state is */
73 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
77 BOOLEAN LockChange
= FALSE
;
79 /* Must be e kernel address */
80 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
82 /* Check if the page is valid */
83 while (!MmIsAddressValid(VirtualAddress
))
85 /* Release the PFN database */
86 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
89 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
90 if (!NT_SUCCESS(Status
))
92 /* This should not fail */
93 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
97 (ULONG_PTR
)VirtualAddress
);
100 /* This flag will be useful later when we do better locking */
103 /* Lock the PFN database */
104 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
107 /* Let caller know what the lock state is */
113 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
114 IN PFN_NUMBER PageCount
,
116 OUT PPFN_NUMBER ValidPages
)
118 PFN_NUMBER ActualPages
= 0;
119 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 PT refcount for: %p\n", Pfn2
);
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_PDE(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
, PointerPde
, 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
,
1102 PMMPTE PointerPte
, PointerPde
;
1104 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1105 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1106 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1107 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1109 /* Only normal VADs supported */
1110 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1112 /* Get the PDE and PTE for the address */
1113 PointerPde
= MiAddressToPde(Va
);
1114 PointerPte
= MiAddressToPte(Va
);
1116 /* Return the next range */
1117 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1119 /* Loop to make sure the PDE is valid */
1125 /* Is the PDE empty? */
1126 if (!PointerPde
->u
.Long
)
1128 /* No address in this range used yet, move to the next PDE range */
1129 *NextVa
= MiPteToAddress(MiPteToAddress(PointerPde
+ 1));
1133 /* The PDE is empty, but is it faulted in? */
1134 if (!PointerPde
->u
.Hard
.Valid
)
1136 /* It isn't, go ahead and do the fault */
1137 LockChange
= MiMakeSystemAddressValid(MiPteToAddress(PointerPde
),
1141 /* Check if the PDE was faulted in, making the PTE readable */
1142 if (!LockChange
) ValidPte
= TRUE
;
1143 } while (LockChange
);
1145 /* Is it safe to try reading the PTE? */
1148 /* FIXME: watch out for large pages */
1150 /* Capture the PTE */
1151 TempPte
= *PointerPte
;
1154 /* The PTE is valid, so it's not zeroed out */
1155 DemandZeroPte
= FALSE
;
1157 /* Check if it's valid or has a valid protection mask */
1158 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1159 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1160 (TempPte
.u
.Hard
.Valid
== 1))
1162 /* This means it's committed */
1165 /* Get protection state of this page */
1166 Protect
= MiGetPageProtection(PointerPte
);
1170 /* Otherwise our defaults should hold */
1171 ASSERT(Protect
== 0);
1172 ASSERT(State
== MEM_RESERVE
);
1177 /* Check if this was a demand-zero PTE, since we need to find the state */
1180 /* Check if the VAD is for committed memory */
1181 if (Vad
->u
.VadFlags
.MemCommit
)
1183 /* This is committed memory */
1186 /* Convert the protection */
1187 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1191 /* Return the protection code */
1192 *ReturnedProtect
= Protect
;
1196 /* PUBLIC FUNCTIONS ***********************************************************/
1203 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1214 MmSecureVirtualMemory(IN PVOID Address
,
1218 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1227 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1229 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1232 /* SYSTEM CALLS ***************************************************************/
1236 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1237 IN PVOID BaseAddress
,
1239 IN SIZE_T NumberOfBytesToRead
,
1240 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1242 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1244 NTSTATUS Status
= STATUS_SUCCESS
;
1245 SIZE_T BytesRead
= 0;
1249 // Check if we came from user mode
1251 if (PreviousMode
!= KernelMode
)
1254 // Validate the read addresses
1256 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1257 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1258 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1259 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1262 // Don't allow to write into kernel space
1264 return STATUS_ACCESS_VIOLATION
;
1268 // Enter SEH for probe
1273 // Probe the output value
1275 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1277 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1280 // Get exception code
1282 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1288 // Don't do zero-byte transfers
1290 if (NumberOfBytesToRead
)
1293 // Reference the process
1295 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1301 if (NT_SUCCESS(Status
))
1306 Status
= MmCopyVirtualMemory(Process
,
1308 PsGetCurrentProcess(),
1310 NumberOfBytesToRead
,
1315 // Dereference the process
1317 ObDereferenceObject(Process
);
1322 // Check if the caller sent this parameter
1324 if (NumberOfBytesRead
)
1327 // Enter SEH to guard write
1332 // Return the number of bytes read
1334 *NumberOfBytesRead
= BytesRead
;
1336 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1350 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1351 IN PVOID BaseAddress
,
1353 IN SIZE_T NumberOfBytesToWrite
,
1354 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1356 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1358 NTSTATUS Status
= STATUS_SUCCESS
;
1359 SIZE_T BytesWritten
= 0;
1363 // Check if we came from user mode
1365 if (PreviousMode
!= KernelMode
)
1368 // Validate the read addresses
1370 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1371 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1372 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1373 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1376 // Don't allow to write into kernel space
1378 return STATUS_ACCESS_VIOLATION
;
1382 // Enter SEH for probe
1387 // Probe the output value
1389 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1391 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1394 // Get exception code
1396 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1402 // Don't do zero-byte transfers
1404 if (NumberOfBytesToWrite
)
1407 // Reference the process
1409 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1415 if (NT_SUCCESS(Status
))
1420 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1424 NumberOfBytesToWrite
,
1429 // Dereference the process
1431 ObDereferenceObject(Process
);
1436 // Check if the caller sent this parameter
1438 if (NumberOfBytesWritten
)
1441 // Enter SEH to guard write
1446 // Return the number of bytes written
1448 *NumberOfBytesWritten
= BytesWritten
;
1450 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1464 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1465 IN OUT PVOID
*UnsafeBaseAddress
,
1466 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1467 IN ULONG NewAccessProtection
,
1468 OUT PULONG UnsafeOldAccessProtection
)
1471 ULONG OldAccessProtection
;
1473 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1474 PVOID BaseAddress
= NULL
;
1475 SIZE_T NumberOfBytesToProtect
= 0;
1476 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1478 BOOLEAN Attached
= FALSE
;
1479 KAPC_STATE ApcState
;
1483 // Check for valid protection flags
1485 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1486 if (Protection
!= PAGE_NOACCESS
&&
1487 Protection
!= PAGE_READONLY
&&
1488 Protection
!= PAGE_READWRITE
&&
1489 Protection
!= PAGE_WRITECOPY
&&
1490 Protection
!= PAGE_EXECUTE
&&
1491 Protection
!= PAGE_EXECUTE_READ
&&
1492 Protection
!= PAGE_EXECUTE_READWRITE
&&
1493 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1498 return STATUS_INVALID_PAGE_PROTECTION
;
1502 // Check if we came from user mode
1504 if (PreviousMode
!= KernelMode
)
1507 // Enter SEH for probing
1512 // Validate all outputs
1514 ProbeForWritePointer(UnsafeBaseAddress
);
1515 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1516 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1521 BaseAddress
= *UnsafeBaseAddress
;
1522 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1524 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1527 // Get exception code
1529 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1538 BaseAddress
= *UnsafeBaseAddress
;
1539 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1543 // Catch illegal base address
1545 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1548 // Catch illegal region size
1550 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1555 return STATUS_INVALID_PARAMETER_3
;
1559 // 0 is also illegal
1561 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1564 // Get a reference to the process
1566 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1567 PROCESS_VM_OPERATION
,
1572 if (!NT_SUCCESS(Status
)) return Status
;
1575 // Check if we should attach
1577 if (CurrentProcess
!= Process
)
1582 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1587 // Do the actual work
1589 Status
= MiProtectVirtualMemory(Process
,
1591 &NumberOfBytesToProtect
,
1592 NewAccessProtection
,
1593 &OldAccessProtection
);
1598 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1601 // Release reference
1603 ObDereferenceObject(Process
);
1606 // Enter SEH to return data
1611 // Return data to user
1613 *UnsafeOldAccessProtection
= OldAccessProtection
;
1614 *UnsafeBaseAddress
= BaseAddress
;
1615 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1630 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1631 IN OUT PVOID
*BaseAddress
,
1632 IN OUT PSIZE_T NumberOfBytesToLock
,
1636 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1638 BOOLEAN Attached
= FALSE
;
1639 KAPC_STATE ApcState
;
1640 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1641 PVOID CapturedBaseAddress
;
1642 SIZE_T CapturedBytesToLock
;
1648 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1651 // Invalid set of flags
1653 return STATUS_INVALID_PARAMETER
;
1657 // At least one flag must be specified
1659 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1664 return STATUS_INVALID_PARAMETER
;
1668 // Enter SEH for probing
1673 // Validate output data
1675 ProbeForWritePointer(BaseAddress
);
1676 ProbeForWriteSize_t(NumberOfBytesToLock
);
1681 CapturedBaseAddress
= *BaseAddress
;
1682 CapturedBytesToLock
= *NumberOfBytesToLock
;
1684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1687 // Get exception code
1689 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1694 // Catch illegal base address
1696 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1699 // Catch illegal region size
1701 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1706 return STATUS_INVALID_PARAMETER
;
1710 // 0 is also illegal
1712 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1715 // Get a reference to the process
1717 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1718 PROCESS_VM_OPERATION
,
1723 if (!NT_SUCCESS(Status
)) return Status
;
1726 // Check if this is is system-mapped
1728 if (MapType
& MAP_SYSTEM
)
1731 // Check for required privilege
1733 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1736 // Fail: Don't have it
1738 ObDereferenceObject(Process
);
1739 return STATUS_PRIVILEGE_NOT_HELD
;
1744 // Check if we should attach
1746 if (CurrentProcess
!= Process
)
1751 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1763 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1766 // Release reference
1768 ObDereferenceObject(Process
);
1771 // Enter SEH to return data
1776 // Return data to user
1778 *BaseAddress
= CapturedBaseAddress
;
1779 *NumberOfBytesToLock
= 0;
1781 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1784 // Get exception code
1786 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1793 return STATUS_SUCCESS
;
1798 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1799 IN OUT PVOID
*BaseAddress
,
1800 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1804 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1806 BOOLEAN Attached
= FALSE
;
1807 KAPC_STATE ApcState
;
1808 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1809 PVOID CapturedBaseAddress
;
1810 SIZE_T CapturedBytesToUnlock
;
1816 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1819 // Invalid set of flags
1821 return STATUS_INVALID_PARAMETER
;
1825 // At least one flag must be specified
1827 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1832 return STATUS_INVALID_PARAMETER
;
1836 // Enter SEH for probing
1841 // Validate output data
1843 ProbeForWritePointer(BaseAddress
);
1844 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1849 CapturedBaseAddress
= *BaseAddress
;
1850 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1852 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1855 // Get exception code
1857 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1862 // Catch illegal base address
1864 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1867 // Catch illegal region size
1869 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1874 return STATUS_INVALID_PARAMETER
;
1878 // 0 is also illegal
1880 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1883 // Get a reference to the process
1885 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1886 PROCESS_VM_OPERATION
,
1891 if (!NT_SUCCESS(Status
)) return Status
;
1894 // Check if this is is system-mapped
1896 if (MapType
& MAP_SYSTEM
)
1899 // Check for required privilege
1901 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1904 // Fail: Don't have it
1906 ObDereferenceObject(Process
);
1907 return STATUS_PRIVILEGE_NOT_HELD
;
1912 // Check if we should attach
1914 if (CurrentProcess
!= Process
)
1919 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1931 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1934 // Release reference
1936 ObDereferenceObject(Process
);
1939 // Enter SEH to return data
1944 // Return data to user
1946 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1947 *NumberOfBytesToUnlock
= 0;
1949 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1952 // Get exception code
1954 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1961 return STATUS_SUCCESS
;
1966 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1967 IN OUT PVOID
*BaseAddress
,
1968 IN OUT PSIZE_T NumberOfBytesToFlush
,
1969 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1973 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1974 PVOID CapturedBaseAddress
;
1975 SIZE_T CapturedBytesToFlush
;
1976 IO_STATUS_BLOCK LocalStatusBlock
;
1980 // Check if we came from user mode
1982 if (PreviousMode
!= KernelMode
)
1985 // Enter SEH for probing
1990 // Validate all outputs
1992 ProbeForWritePointer(BaseAddress
);
1993 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1994 ProbeForWriteIoStatusBlock(IoStatusBlock
);
1999 CapturedBaseAddress
= *BaseAddress
;
2000 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2002 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2005 // Get exception code
2007 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2016 CapturedBaseAddress
= *BaseAddress
;
2017 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2021 // Catch illegal base address
2023 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2026 // Catch illegal region size
2028 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2033 return STATUS_INVALID_PARAMETER
;
2037 // Get a reference to the process
2039 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2040 PROCESS_VM_OPERATION
,
2045 if (!NT_SUCCESS(Status
)) return Status
;
2050 Status
= MmFlushVirtualMemory(Process
,
2051 &CapturedBaseAddress
,
2052 &CapturedBytesToFlush
,
2056 // Release reference
2058 ObDereferenceObject(Process
);
2061 // Enter SEH to return data
2066 // Return data to user
2068 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2069 *NumberOfBytesToFlush
= 0;
2070 *IoStatusBlock
= LocalStatusBlock
;
2072 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2088 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2090 IN PVOID BaseAddress
,
2091 IN SIZE_T RegionSize
,
2092 IN PVOID
*UserAddressArray
,
2093 OUT PULONG_PTR EntriesInUserAddressArray
,
2094 OUT PULONG Granularity
)
2099 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2100 ULONG_PTR CapturedEntryCount
;
2104 // Check if we came from user mode
2106 if (PreviousMode
!= KernelMode
)
2109 // Enter SEH for probing
2114 // Catch illegal base address
2116 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2119 // Catch illegal region size
2121 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2126 return STATUS_INVALID_PARAMETER_3
;
2130 // Validate all data
2132 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2133 ProbeForWriteUlong(Granularity
);
2138 CapturedEntryCount
= *EntriesInUserAddressArray
;
2141 // Must have a count
2143 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2146 // Can't be larger than the maximum
2148 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2153 return STATUS_INVALID_PARAMETER_5
;
2157 // Probe the actual array
2159 ProbeForWrite(UserAddressArray
,
2160 CapturedEntryCount
* sizeof(PVOID
),
2163 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2166 // Get exception code
2168 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2177 CapturedEntryCount
= *EntriesInUserAddressArray
;
2178 ASSERT(CapturedEntryCount
!= 0);
2182 // Check if this is a local request
2184 if (ProcessHandle
== NtCurrentProcess())
2187 // No need to reference the process
2189 Process
= PsGetCurrentProcess();
2194 // Reference the target
2196 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2197 PROCESS_VM_OPERATION
,
2202 if (!NT_SUCCESS(Status
)) return Status
;
2206 // Compute the last address and validate it
2208 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2209 if (BaseAddress
> EndAddress
)
2214 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2215 return STATUS_INVALID_PARAMETER_4
;
2224 // Dereference if needed
2226 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2229 // Enter SEH to return data
2234 // Return data to user
2236 *EntriesInUserAddressArray
= 0;
2237 *Granularity
= PAGE_SIZE
;
2239 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2242 // Get exception code
2244 Status
= _SEH2_GetExceptionCode();
2251 return STATUS_SUCCESS
;
2259 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2260 IN PVOID BaseAddress
,
2261 IN SIZE_T RegionSize
)
2266 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2267 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2270 // Catch illegal base address
2272 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2275 // Catch illegal region size
2277 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2282 return STATUS_INVALID_PARAMETER_3
;
2286 // Check if this is a local request
2288 if (ProcessHandle
== NtCurrentProcess())
2291 // No need to reference the process
2293 Process
= PsGetCurrentProcess();
2298 // Reference the target
2300 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2301 PROCESS_VM_OPERATION
,
2306 if (!NT_SUCCESS(Status
)) return Status
;
2310 // Compute the last address and validate it
2312 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2313 if (BaseAddress
> EndAddress
)
2318 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2319 return STATUS_INVALID_PARAMETER_3
;
2328 // Dereference if needed
2330 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2335 return STATUS_SUCCESS
;
2340 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2341 IN PVOID BaseAddress
,
2342 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2343 OUT PVOID MemoryInformation
,
2344 IN SIZE_T MemoryInformationLength
,
2345 OUT PSIZE_T ReturnLength
)
2347 PEPROCESS TargetProcess
;
2350 PVOID Address
, NextAddress
;
2351 BOOLEAN Found
= FALSE
;
2352 ULONG NewProtect
, NewState
, BaseVpn
;
2353 MEMORY_BASIC_INFORMATION MemoryInfo
;
2354 KAPC_STATE ApcState
;
2355 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2357 /* Only this class is supported for now */
2358 ASSERT(MemoryInformationClass
== MemoryBasicInformation
);
2360 /* Validate the size information of the class */
2361 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2363 /* The size is invalid */
2364 return STATUS_INFO_LENGTH_MISMATCH
;
2367 /* Bail out if the address is invalid */
2368 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2370 /* Check for illegal addresses in user-space, or the shared memory area */
2371 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2372 (PAGE_ALIGN(BaseAddress
) == (PVOID
)USER_SHARED_DATA
))
2374 Address
= PAGE_ALIGN(BaseAddress
);
2376 /* Make up an info structure describing this range */
2377 MemoryInfo
.BaseAddress
= Address
;
2378 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
2379 MemoryInfo
.Type
= MEM_PRIVATE
;
2381 /* Special case for shared data */
2382 if (Address
== (PVOID
)USER_SHARED_DATA
)
2384 MemoryInfo
.AllocationBase
= (PVOID
)USER_SHARED_DATA
;
2385 MemoryInfo
.State
= MEM_COMMIT
;
2386 MemoryInfo
.Protect
= PAGE_READONLY
;
2387 MemoryInfo
.RegionSize
= PAGE_SIZE
;
2391 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
2392 MemoryInfo
.State
= MEM_RESERVE
;
2393 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2394 MemoryInfo
.RegionSize
= (ULONG_PTR
)MemoryInfo
.AllocationBase
- (ULONG_PTR
)Address
;
2397 /* Return the data (FIXME: Use SEH) */
2398 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2399 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2401 return STATUS_SUCCESS
;
2404 /* Check if this is for a local or remote process */
2405 if (ProcessHandle
== NtCurrentProcess())
2407 TargetProcess
= PsGetCurrentProcess();
2411 /* Reference the target process */
2412 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2413 PROCESS_QUERY_INFORMATION
,
2415 ExGetPreviousMode(),
2416 (PVOID
*)&TargetProcess
,
2418 if (!NT_SUCCESS(Status
)) return Status
;
2420 /* Attach to it now */
2421 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2425 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2426 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2428 /* Scan on the right */
2429 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2430 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2433 /* Check if this VAD covers the allocation range */
2434 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2435 (BaseVpn
<= Vad
->EndingVpn
))
2442 /* Check if this VAD is too high */
2443 if (BaseVpn
< Vad
->StartingVpn
)
2445 /* Search on the left next */
2446 Vad
= Vad
->LeftChild
;
2450 /* Then this VAD is too low, keep searching on the right */
2451 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2452 Vad
= Vad
->RightChild
;
2457 /* Was a VAD found? */
2460 Address
= PAGE_ALIGN(BaseAddress
);
2462 /* Calculate region size */
2465 if (Vad
->StartingVpn
>= BaseVpn
)
2467 /* Region size is the free space till the start of that VAD */
2468 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2472 /* Get the next VAD */
2473 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
2476 /* Region size is the free space till the start of that VAD */
2477 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2481 /* Maximum possible region size with that base address */
2482 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2488 /* Maximum possible region size with that base address */
2489 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2492 /* Check if we were attached */
2493 if (ProcessHandle
!= NtCurrentProcess())
2495 /* Detach and derefernece the process */
2496 KeUnstackDetachProcess(&ApcState
);
2497 ObDereferenceObject(TargetProcess
);
2500 /* Build the rest of the initial information block */
2501 MemoryInfo
.BaseAddress
= Address
;
2502 MemoryInfo
.AllocationBase
= NULL
;
2503 MemoryInfo
.AllocationProtect
= 0;
2504 MemoryInfo
.State
= MEM_FREE
;
2505 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2506 MemoryInfo
.Type
= 0;
2508 /* Return the data (FIXME: Use SEH) */
2509 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2510 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2512 return STATUS_SUCCESS
;
2515 /* This must be a VM VAD */
2516 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2518 /* Build the initial information block */
2519 Address
= PAGE_ALIGN(BaseAddress
);
2520 MemoryInfo
.BaseAddress
= Address
;
2521 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2522 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2523 MemoryInfo
.Type
= MEM_PRIVATE
;
2525 /* Find the largest chunk of memory which has the same state and protection mask */
2526 MemoryInfo
.State
= MiQueryAddressState(Address
,
2529 &MemoryInfo
.Protect
,
2531 Address
= NextAddress
;
2532 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2534 /* Keep going unless the state or protection mask changed */
2535 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2536 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2537 Address
= NextAddress
;
2540 /* Now that we know the last VA address, calculate hte region size */
2541 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2543 /* Check if we were attached */
2544 if (ProcessHandle
!= NtCurrentProcess())
2546 /* Detach and derefernece the process */
2547 KeUnstackDetachProcess(&ApcState
);
2548 ObDereferenceObject(TargetProcess
);
2551 /* Return the data (FIXME: Use SEH) */
2552 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2553 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2556 DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
2557 "State: %lx Type: %lx Size: %lx\n",
2558 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2559 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2560 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2561 return STATUS_SUCCESS
;