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 MiQueryAddressState(IN PVOID Va
,
1055 IN PEPROCESS TargetProcess
,
1056 OUT PULONG ReturnedProtect
,
1060 PMMPTE PointerPte
, PointerPde
;
1062 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1063 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1064 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1065 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1067 /* Only normal VADs supported */
1068 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1070 /* Get the PDE and PTE for the address */
1071 PointerPde
= MiAddressToPde(Va
);
1072 PointerPte
= MiAddressToPte(Va
);
1074 /* Return the next range */
1075 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1077 /* Loop to make sure the PDE is valid */
1083 /* Is the PDE empty? */
1084 if (!PointerPde
->u
.Long
)
1086 /* No address in this range used yet, move to the next PDE range */
1087 *NextVa
= MiPteToAddress(MiPteToAddress(PointerPde
+ 1));
1091 /* The PDE is empty, but is it faulted in? */
1092 if (!PointerPde
->u
.Hard
.Valid
)
1094 /* It isn't, go ahead and do the fault */
1095 LockChange
= MiMakeSystemAddressValid(MiPteToAddress(PointerPde
),
1099 /* Check if the PDE was faulted in, making the PTE readable */
1100 if (!LockChange
) ValidPte
= TRUE
;
1101 } while (LockChange
);
1103 /* Is it safe to try reading the PTE? */
1106 /* FIXME: watch out for large pages */
1108 /* Capture the PTE */
1109 TempPte
= *PointerPte
;
1112 /* The PTE is valid, so it's not zeroed out */
1113 DemandZeroPte
= FALSE
;
1115 /* Check if it's valid or has a valid protection mask */
1116 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1117 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1118 (TempPte
.u
.Hard
.Valid
== 1))
1120 /* This means it's committed */
1123 /* For now, we lie about the protection */
1124 Protect
= PAGE_EXECUTE_READWRITE
;
1128 /* Otherwise our defaults should hold */
1129 ASSERT(Protect
== 0);
1130 ASSERT(State
== MEM_RESERVE
);
1135 /* Check if this was a demand-zero PTE, since we need to find the state */
1138 /* Check if the VAD is for committed memory */
1139 if (Vad
->u
.VadFlags
.MemCommit
)
1141 /* This is committed memory */
1144 /* Convert the protection */
1145 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1149 /* Return the protection code */
1150 *ReturnedProtect
= Protect
;
1154 /* PUBLIC FUNCTIONS ***********************************************************/
1161 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1172 MmSecureVirtualMemory(IN PVOID Address
,
1176 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1185 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1187 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1190 /* SYSTEM CALLS ***************************************************************/
1194 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1195 IN PVOID BaseAddress
,
1197 IN SIZE_T NumberOfBytesToRead
,
1198 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1200 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1202 NTSTATUS Status
= STATUS_SUCCESS
;
1203 SIZE_T BytesRead
= 0;
1207 // Check if we came from user mode
1209 if (PreviousMode
!= KernelMode
)
1212 // Validate the read addresses
1214 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1215 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1216 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1217 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1220 // Don't allow to write into kernel space
1222 return STATUS_ACCESS_VIOLATION
;
1226 // Enter SEH for probe
1231 // Probe the output value
1233 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1235 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1238 // Get exception code
1240 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1246 // Don't do zero-byte transfers
1248 if (NumberOfBytesToRead
)
1251 // Reference the process
1253 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1259 if (NT_SUCCESS(Status
))
1264 Status
= MmCopyVirtualMemory(Process
,
1266 PsGetCurrentProcess(),
1268 NumberOfBytesToRead
,
1273 // Dereference the process
1275 ObDereferenceObject(Process
);
1280 // Check if the caller sent this parameter
1282 if (NumberOfBytesRead
)
1285 // Enter SEH to guard write
1290 // Return the number of bytes read
1292 *NumberOfBytesRead
= BytesRead
;
1294 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1308 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1309 IN PVOID BaseAddress
,
1311 IN SIZE_T NumberOfBytesToWrite
,
1312 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1314 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1316 NTSTATUS Status
= STATUS_SUCCESS
;
1317 SIZE_T BytesWritten
= 0;
1321 // Check if we came from user mode
1323 if (PreviousMode
!= KernelMode
)
1326 // Validate the read addresses
1328 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1329 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1330 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1331 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1334 // Don't allow to write into kernel space
1336 return STATUS_ACCESS_VIOLATION
;
1340 // Enter SEH for probe
1345 // Probe the output value
1347 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1349 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1352 // Get exception code
1354 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1360 // Don't do zero-byte transfers
1362 if (NumberOfBytesToWrite
)
1365 // Reference the process
1367 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1373 if (NT_SUCCESS(Status
))
1378 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1382 NumberOfBytesToWrite
,
1387 // Dereference the process
1389 ObDereferenceObject(Process
);
1394 // Check if the caller sent this parameter
1396 if (NumberOfBytesWritten
)
1399 // Enter SEH to guard write
1404 // Return the number of bytes written
1406 *NumberOfBytesWritten
= BytesWritten
;
1408 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1422 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1423 IN OUT PVOID
*UnsafeBaseAddress
,
1424 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1425 IN ULONG NewAccessProtection
,
1426 OUT PULONG UnsafeOldAccessProtection
)
1429 ULONG OldAccessProtection
;
1431 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1432 PVOID BaseAddress
= NULL
;
1433 SIZE_T NumberOfBytesToProtect
= 0;
1434 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1436 BOOLEAN Attached
= FALSE
;
1437 KAPC_STATE ApcState
;
1441 // Check for valid protection flags
1443 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1444 if (Protection
!= PAGE_NOACCESS
&&
1445 Protection
!= PAGE_READONLY
&&
1446 Protection
!= PAGE_READWRITE
&&
1447 Protection
!= PAGE_WRITECOPY
&&
1448 Protection
!= PAGE_EXECUTE
&&
1449 Protection
!= PAGE_EXECUTE_READ
&&
1450 Protection
!= PAGE_EXECUTE_READWRITE
&&
1451 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1456 return STATUS_INVALID_PAGE_PROTECTION
;
1460 // Check if we came from user mode
1462 if (PreviousMode
!= KernelMode
)
1465 // Enter SEH for probing
1470 // Validate all outputs
1472 ProbeForWritePointer(UnsafeBaseAddress
);
1473 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1474 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1479 BaseAddress
= *UnsafeBaseAddress
;
1480 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1482 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1485 // Get exception code
1487 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1496 BaseAddress
= *UnsafeBaseAddress
;
1497 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1501 // Catch illegal base address
1503 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1506 // Catch illegal region size
1508 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1513 return STATUS_INVALID_PARAMETER_3
;
1517 // 0 is also illegal
1519 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1522 // Get a reference to the process
1524 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1525 PROCESS_VM_OPERATION
,
1530 if (!NT_SUCCESS(Status
)) return Status
;
1533 // Check if we should attach
1535 if (CurrentProcess
!= Process
)
1540 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1545 // Do the actual work
1547 Status
= MiProtectVirtualMemory(Process
,
1549 &NumberOfBytesToProtect
,
1550 NewAccessProtection
,
1551 &OldAccessProtection
);
1556 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1559 // Release reference
1561 ObDereferenceObject(Process
);
1564 // Enter SEH to return data
1569 // Return data to user
1571 *UnsafeOldAccessProtection
= OldAccessProtection
;
1572 *UnsafeBaseAddress
= BaseAddress
;
1573 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1588 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1589 IN OUT PVOID
*BaseAddress
,
1590 IN OUT PSIZE_T NumberOfBytesToLock
,
1594 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1596 BOOLEAN Attached
= FALSE
;
1597 KAPC_STATE ApcState
;
1598 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1599 PVOID CapturedBaseAddress
;
1600 SIZE_T CapturedBytesToLock
;
1606 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1609 // Invalid set of flags
1611 return STATUS_INVALID_PARAMETER
;
1615 // At least one flag must be specified
1617 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1622 return STATUS_INVALID_PARAMETER
;
1626 // Enter SEH for probing
1631 // Validate output data
1633 ProbeForWritePointer(BaseAddress
);
1634 ProbeForWriteSize_t(NumberOfBytesToLock
);
1639 CapturedBaseAddress
= *BaseAddress
;
1640 CapturedBytesToLock
= *NumberOfBytesToLock
;
1642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1645 // Get exception code
1647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1652 // Catch illegal base address
1654 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1657 // Catch illegal region size
1659 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1664 return STATUS_INVALID_PARAMETER
;
1668 // 0 is also illegal
1670 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1673 // Get a reference to the process
1675 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1676 PROCESS_VM_OPERATION
,
1681 if (!NT_SUCCESS(Status
)) return Status
;
1684 // Check if this is is system-mapped
1686 if (MapType
& MAP_SYSTEM
)
1689 // Check for required privilege
1691 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1694 // Fail: Don't have it
1696 ObDereferenceObject(Process
);
1697 return STATUS_PRIVILEGE_NOT_HELD
;
1702 // Check if we should attach
1704 if (CurrentProcess
!= Process
)
1709 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1721 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1724 // Release reference
1726 ObDereferenceObject(Process
);
1729 // Enter SEH to return data
1734 // Return data to user
1736 *BaseAddress
= CapturedBaseAddress
;
1737 *NumberOfBytesToLock
= 0;
1739 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1742 // Get exception code
1744 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1751 return STATUS_SUCCESS
;
1756 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1757 IN OUT PVOID
*BaseAddress
,
1758 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1762 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1764 BOOLEAN Attached
= FALSE
;
1765 KAPC_STATE ApcState
;
1766 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1767 PVOID CapturedBaseAddress
;
1768 SIZE_T CapturedBytesToUnlock
;
1774 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1777 // Invalid set of flags
1779 return STATUS_INVALID_PARAMETER
;
1783 // At least one flag must be specified
1785 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1790 return STATUS_INVALID_PARAMETER
;
1794 // Enter SEH for probing
1799 // Validate output data
1801 ProbeForWritePointer(BaseAddress
);
1802 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1807 CapturedBaseAddress
= *BaseAddress
;
1808 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1810 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1813 // Get exception code
1815 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1820 // Catch illegal base address
1822 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1825 // Catch illegal region size
1827 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1832 return STATUS_INVALID_PARAMETER
;
1836 // 0 is also illegal
1838 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1841 // Get a reference to the process
1843 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1844 PROCESS_VM_OPERATION
,
1849 if (!NT_SUCCESS(Status
)) return Status
;
1852 // Check if this is is system-mapped
1854 if (MapType
& MAP_SYSTEM
)
1857 // Check for required privilege
1859 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1862 // Fail: Don't have it
1864 ObDereferenceObject(Process
);
1865 return STATUS_PRIVILEGE_NOT_HELD
;
1870 // Check if we should attach
1872 if (CurrentProcess
!= Process
)
1877 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1889 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1892 // Release reference
1894 ObDereferenceObject(Process
);
1897 // Enter SEH to return data
1902 // Return data to user
1904 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1905 *NumberOfBytesToUnlock
= 0;
1907 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1910 // Get exception code
1912 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1919 return STATUS_SUCCESS
;
1924 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1925 IN OUT PVOID
*BaseAddress
,
1926 IN OUT PSIZE_T NumberOfBytesToFlush
,
1927 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1931 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1932 PVOID CapturedBaseAddress
;
1933 SIZE_T CapturedBytesToFlush
;
1934 IO_STATUS_BLOCK LocalStatusBlock
;
1938 // Check if we came from user mode
1940 if (PreviousMode
!= KernelMode
)
1943 // Enter SEH for probing
1948 // Validate all outputs
1950 ProbeForWritePointer(BaseAddress
);
1951 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1952 ProbeForWriteIoStatusBlock(IoStatusBlock
);
1957 CapturedBaseAddress
= *BaseAddress
;
1958 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1960 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1963 // Get exception code
1965 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1974 CapturedBaseAddress
= *BaseAddress
;
1975 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1979 // Catch illegal base address
1981 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1984 // Catch illegal region size
1986 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
1991 return STATUS_INVALID_PARAMETER
;
1995 // Get a reference to the process
1997 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1998 PROCESS_VM_OPERATION
,
2003 if (!NT_SUCCESS(Status
)) return Status
;
2008 Status
= MmFlushVirtualMemory(Process
,
2009 &CapturedBaseAddress
,
2010 &CapturedBytesToFlush
,
2014 // Release reference
2016 ObDereferenceObject(Process
);
2019 // Enter SEH to return data
2024 // Return data to user
2026 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2027 *NumberOfBytesToFlush
= 0;
2028 *IoStatusBlock
= LocalStatusBlock
;
2030 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2046 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2048 IN PVOID BaseAddress
,
2049 IN SIZE_T RegionSize
,
2050 IN PVOID
*UserAddressArray
,
2051 OUT PULONG_PTR EntriesInUserAddressArray
,
2052 OUT PULONG Granularity
)
2057 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2058 ULONG_PTR CapturedEntryCount
;
2062 // Check if we came from user mode
2064 if (PreviousMode
!= KernelMode
)
2067 // Enter SEH for probing
2072 // Catch illegal base address
2074 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2077 // Catch illegal region size
2079 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2084 return STATUS_INVALID_PARAMETER_3
;
2088 // Validate all data
2090 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2091 ProbeForWriteUlong(Granularity
);
2096 CapturedEntryCount
= *EntriesInUserAddressArray
;
2099 // Must have a count
2101 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2104 // Can't be larger than the maximum
2106 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2111 return STATUS_INVALID_PARAMETER_5
;
2115 // Probe the actual array
2117 ProbeForWrite(UserAddressArray
,
2118 CapturedEntryCount
* sizeof(PVOID
),
2121 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2124 // Get exception code
2126 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2135 CapturedEntryCount
= *EntriesInUserAddressArray
;
2136 ASSERT(CapturedEntryCount
!= 0);
2140 // Check if this is a local request
2142 if (ProcessHandle
== NtCurrentProcess())
2145 // No need to reference the process
2147 Process
= PsGetCurrentProcess();
2152 // Reference the target
2154 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2155 PROCESS_VM_OPERATION
,
2160 if (!NT_SUCCESS(Status
)) return Status
;
2164 // Compute the last address and validate it
2166 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2167 if (BaseAddress
> EndAddress
)
2172 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2173 return STATUS_INVALID_PARAMETER_4
;
2182 // Dereference if needed
2184 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2187 // Enter SEH to return data
2192 // Return data to user
2194 *EntriesInUserAddressArray
= 0;
2195 *Granularity
= PAGE_SIZE
;
2197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2200 // Get exception code
2202 Status
= _SEH2_GetExceptionCode();
2209 return STATUS_SUCCESS
;
2217 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2218 IN PVOID BaseAddress
,
2219 IN SIZE_T RegionSize
)
2224 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2225 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2228 // Catch illegal base address
2230 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2233 // Catch illegal region size
2235 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2240 return STATUS_INVALID_PARAMETER_3
;
2244 // Check if this is a local request
2246 if (ProcessHandle
== NtCurrentProcess())
2249 // No need to reference the process
2251 Process
= PsGetCurrentProcess();
2256 // Reference the target
2258 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2259 PROCESS_VM_OPERATION
,
2264 if (!NT_SUCCESS(Status
)) return Status
;
2268 // Compute the last address and validate it
2270 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2271 if (BaseAddress
> EndAddress
)
2276 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2277 return STATUS_INVALID_PARAMETER_3
;
2286 // Dereference if needed
2288 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2293 return STATUS_SUCCESS
;
2298 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2299 IN PVOID BaseAddress
,
2300 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2301 OUT PVOID MemoryInformation
,
2302 IN SIZE_T MemoryInformationLength
,
2303 OUT PSIZE_T ReturnLength
)
2305 PEPROCESS TargetProcess
;
2308 PVOID Address
, NextAddress
;
2310 ULONG NewProtect
, NewState
, BaseVpn
;
2311 MEMORY_BASIC_INFORMATION MemoryInfo
;
2312 KAPC_STATE ApcState
;
2313 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2315 /* Only this class is supported for now */
2316 ASSERT(MemoryInformationClass
== MemoryBasicInformation
);
2318 /* Validate the size information of the class */
2319 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2321 /* The size is invalid */
2322 return STATUS_INFO_LENGTH_MISMATCH
;
2325 /* Bail out if the address is invalid */
2326 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2328 /* Check for illegal addresses in user-space, or the shared memory area */
2329 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2330 (PAGE_ALIGN(BaseAddress
) == (PVOID
)USER_SHARED_DATA
))
2332 /* FIXME: We should return some bogus info structure */
2337 /* Check if this is for a local or remote process */
2338 if (ProcessHandle
== NtCurrentProcess())
2340 TargetProcess
= PsGetCurrentProcess();
2344 /* Reference the target process */
2345 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2346 PROCESS_QUERY_INFORMATION
,
2348 ExGetPreviousMode(),
2349 (PVOID
*)&TargetProcess
,
2351 if (!NT_SUCCESS(Status
)) return Status
;
2353 /* Attach to it now */
2354 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2358 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2359 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2361 /* Scan on the right */
2362 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2363 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2366 /* Check if this VAD covers the allocation range */
2367 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2368 (BaseVpn
<= Vad
->EndingVpn
))
2375 /* Check if this VAD is too high */
2376 if (BaseVpn
< Vad
->StartingVpn
)
2378 /* Search on the left next */
2379 Vad
= Vad
->LeftChild
;
2383 /* Then this VAD is too low, keep searching on the right */
2384 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2385 Vad
= Vad
->RightChild
;
2390 /* Was a VAD found? */
2393 /* We don't handle this yet */
2398 /* This must be a VM VAD */
2399 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2401 /* Build the initial information block */
2402 Address
= PAGE_ALIGN(BaseAddress
);
2403 MemoryInfo
.BaseAddress
= Address
;
2404 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2405 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2406 MemoryInfo
.Type
= MEM_PRIVATE
;
2408 /* Find the largest chunk of memory which has the same state and protection mask */
2409 MemoryInfo
.State
= MiQueryAddressState(Address
,
2412 &MemoryInfo
.Protect
,
2414 Address
= NextAddress
;
2415 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2417 /* Keep going unless the state or protection mask changed */
2418 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2419 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2420 Address
= NextAddress
;
2423 /* Now that we know the last VA address, calculate hte region size */
2424 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2426 /* Check if we were attached */
2427 if (ProcessHandle
!= NtCurrentProcess())
2429 /* Detach and derefernece the process */
2430 KeUnstackDetachProcess(&ApcState
);
2431 ObDereferenceObject(TargetProcess
);
2434 /* Return the data (FIXME: Use SEH) */
2435 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2436 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2439 DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
2440 "State: %lx Type: %lx Size: %lx\n",
2441 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2442 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2443 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2444 return STATUS_SUCCESS
;