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
= MiPteToPde(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 /* Drop the reference on the page table. */
262 MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde
)), PFN_FROM_PTE(PointerPde
));
264 /* Drop the share count */
265 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
267 /* Either a fork, or this is the shared user data page */
268 if (PointerPte
<= MiHighestUserPte
)
270 /* If it's not the shared user page, then crash, since there's no fork() yet */
271 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
272 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
274 /* Must be some sort of memory corruption */
275 KeBugCheckEx(MEMORY_MANAGEMENT
,
277 (ULONG_PTR
)PointerPte
,
278 (ULONG_PTR
)PrototypePte
,
279 (ULONG_PTR
)Pfn1
->PteAddress
);
285 /* Make sure the saved PTE address is valid */
286 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
288 /* The PFN entry is illegal, or invalid */
289 KeBugCheckEx(MEMORY_MANAGEMENT
,
291 (ULONG_PTR
)PointerPte
,
293 (ULONG_PTR
)Pfn1
->PteAddress
);
296 /* There should only be 1 shared reference count */
297 ASSERT(Pfn1
->u2
.ShareCount
== 1);
299 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
300 //DPRINT1("Dropping a ref...\n");
301 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
303 /* Mark the PFN for deletion and dereference what should be the last ref */
304 MI_SET_PFN_DELETED(Pfn1
);
305 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
307 /* We should eventually do this */
308 //CurrentProcess->NumberOfPrivatePages--;
311 /* Destroy the PTE and flush the TLB */
312 PointerPte
->u
.Long
= 0;
318 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
319 IN ULONG_PTR EndingAddress
,
322 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
325 PEPROCESS CurrentProcess
;
327 BOOLEAN AddressGap
= FALSE
;
328 PSUBSECTION Subsection
;
329 PUSHORT UsedPageTableEntries
;
331 /* Get out if this is a fake VAD, RosMm will free the marea pages */
332 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
334 /* Grab the process and PTE/PDE for the address being deleted */
335 CurrentProcess
= PsGetCurrentProcess();
336 PointerPde
= MiAddressToPde(Va
);
337 PointerPte
= MiAddressToPte(Va
);
339 /* Check if this is a section VAD or a VM VAD */
340 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
342 /* Don't worry about prototypes */
343 PrototypePte
= LastPrototypePte
= NULL
;
347 /* Get the prototype PTE */
348 PrototypePte
= Vad
->FirstPrototypePte
;
349 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
352 /* In all cases, we don't support fork() yet */
353 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
355 /* Loop the PTE for each VA */
358 /* First keep going until we find a valid PDE */
359 while (!PointerPde
->u
.Long
)
361 /* There are gaps in the address space */
364 /* Still no valid PDE, try the next 4MB (or whatever) */
367 /* Update the PTE on this new boundary */
368 PointerPte
= MiPteToAddress(PointerPde
);
370 /* Check if all the PDEs are invalid, so there's nothing to free */
371 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
372 if (Va
> EndingAddress
) return;
375 /* Now check if the PDE is mapped in */
376 if (!PointerPde
->u
.Hard
.Valid
)
378 /* It isn't, so map it in */
379 PointerPte
= MiPteToAddress(PointerPde
);
380 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
383 /* Now we should have a valid PDE, mapped in, and still have some VA */
384 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
385 ASSERT(Va
<= EndingAddress
);
386 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
388 /* Check if this is a section VAD with gaps in it */
389 if ((AddressGap
) && (LastPrototypePte
))
391 /* We need to skip to the next correct prototype PTE */
392 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
394 /* And we need the subsection to skip to the next last prototype PTE */
395 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
399 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
403 /* No more subsections, we are done with prototype PTEs */
408 /* Lock the PFN Database while we delete the PTEs */
409 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
412 /* Capture the PDE and make sure it exists */
413 TempPte
= *PointerPte
;
416 DPRINT("Decrement used PTEs by address: %lx\n", Va
);
417 (*UsedPageTableEntries
)--;
418 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
419 DPRINT("Refs: %lx\n", (*UsedPageTableEntries
));
421 /* Check if the PTE is actually mapped in */
422 if (TempPte
.u
.Long
& 0xFFFFFC01)
424 /* Are we dealing with section VAD? */
425 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
427 /* We need to skip to the next correct prototype PTE */
428 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
430 /* And we need the subsection to skip to the next last prototype PTE */
431 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
435 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
439 /* No more subsections, we are done with prototype PTEs */
444 /* Check for prototype PTE */
445 if ((TempPte
.u
.Hard
.Valid
== 0) &&
446 (TempPte
.u
.Soft
.Prototype
== 1))
449 PointerPte
->u
.Long
= 0;
453 /* Delete the PTE proper */
454 MiDeletePte(PointerPte
,
462 /* The PTE was never mapped, just nuke it here */
463 PointerPte
->u
.Long
= 0;
467 /* Update the address and PTE for it */
472 /* Making sure the PDE is still valid */
473 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
475 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
477 /* The PDE should still be valid at this point */
478 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
480 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va
, PointerPde
->u
.Hard
.PageFrameNumber
);
481 if (!(*UsedPageTableEntries
))
483 DPRINT("They are!\n");
484 if (PointerPde
->u
.Long
!= 0)
486 DPRINT("PDE active: %lx in %16s\n", PointerPde
->u
.Hard
.PageFrameNumber
, CurrentProcess
->ImageFileName
);
488 /* Delete the PTE proper */
489 MiDeletePte(PointerPde
,
490 MiPteToAddress(PointerPde
),
496 /* Release the lock and get out if we're done */
497 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
498 if (Va
> EndingAddress
) return;
500 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
501 PointerPde
= MiAddressToPde(Va
);
507 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
508 OUT PBOOLEAN HaveBadAddress
,
509 OUT PULONG_PTR BadAddress
)
511 PEXCEPTION_RECORD ExceptionRecord
;
517 *HaveBadAddress
= FALSE
;
520 // Get the exception record
522 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
525 // Look at the exception code
527 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
528 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
529 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
532 // We can tell the address if we have more than one parameter
534 if (ExceptionRecord
->NumberParameters
> 1)
537 // Return the address
539 *HaveBadAddress
= TRUE
;
540 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
545 // Continue executing the next handler
547 return EXCEPTION_EXECUTE_HANDLER
;
552 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
553 IN PVOID SourceAddress
,
554 IN PEPROCESS TargetProcess
,
555 OUT PVOID TargetAddress
,
556 IN SIZE_T BufferSize
,
557 IN KPROCESSOR_MODE PreviousMode
,
558 OUT PSIZE_T ReturnSize
)
560 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
561 PMDL Mdl
= (PMDL
)MdlBuffer
;
562 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
563 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
564 volatile BOOLEAN PagesLocked
;
565 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
566 volatile PVOID MdlAddress
;
568 BOOLEAN HaveBadAddress
;
569 ULONG_PTR BadAddress
;
570 NTSTATUS Status
= STATUS_SUCCESS
;
574 // Calculate the maximum amount of data to move
576 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
577 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
578 CurrentSize
= TotalSize
;
579 RemainingSize
= BufferSize
;
582 // Loop as long as there is still data
584 while (RemainingSize
> 0)
587 // Check if this transfer will finish everything off
589 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
592 // Attach to the source address space
594 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
597 // Reset state for this pass
601 FailedInMoving
= FALSE
;
602 ASSERT(FailedInProbe
== FALSE
);
605 // Protect user-mode copy
610 // If this is our first time, probe the buffer
612 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
615 // Catch a failure here
617 FailedInProbe
= TRUE
;
622 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
627 FailedInProbe
= FALSE
;
631 // Initialize and probe and lock the MDL
633 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
634 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
640 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
649 // Use our SEH handler to pick this up
651 FailedInMapping
= TRUE
;
652 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
656 // Now let go of the source and grab to the target process
658 KeUnstackDetachProcess(&ApcState
);
659 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
662 // Check if this is our first time through
664 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
667 // Catch a failure here
669 FailedInProbe
= TRUE
;
674 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
679 FailedInProbe
= FALSE
;
683 // Now do the actual move
685 FailedInMoving
= TRUE
;
686 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
688 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
693 // Detach from whoever we may be attached to
695 KeUnstackDetachProcess(&ApcState
);
698 // Check if we had mapped the pages
700 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
703 // Check if we had locked the pages
705 if (PagesLocked
) MmUnlockPages(Mdl
);
708 // Check if we hit working set quota
710 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
715 return STATUS_WORKING_SET_QUOTA
;
719 // Check if we failed during the probe or mapping
721 if ((FailedInProbe
) || (FailedInMapping
))
726 Status
= _SEH2_GetExceptionCode();
727 _SEH2_YIELD(return Status
);
731 // Otherwise, we failed probably during the move
733 *ReturnSize
= BufferSize
- RemainingSize
;
737 // Check if we know exactly where we stopped copying
742 // Return the exact number of bytes copied
744 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
749 // Return partial copy
751 Status
= STATUS_PARTIAL_COPY
;
756 // Check for SEH status
758 if (Status
!= STATUS_SUCCESS
) return Status
;
761 // Detach from target
763 KeUnstackDetachProcess(&ApcState
);
768 MmUnmapLockedPages(MdlAddress
, Mdl
);
772 // Update location and size
774 RemainingSize
-= CurrentSize
;
775 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
776 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
782 *ReturnSize
= BufferSize
;
783 return STATUS_SUCCESS
;
788 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
789 IN PVOID SourceAddress
,
790 IN PEPROCESS TargetProcess
,
791 OUT PVOID TargetAddress
,
792 IN SIZE_T BufferSize
,
793 IN KPROCESSOR_MODE PreviousMode
,
794 OUT PSIZE_T ReturnSize
)
796 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
797 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
798 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
799 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
802 BOOLEAN HaveBadAddress
;
803 ULONG_PTR BadAddress
;
804 NTSTATUS Status
= STATUS_SUCCESS
;
808 // Calculate the maximum amount of data to move
810 TotalSize
= MI_MAX_TRANSFER_SIZE
;
811 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
812 CurrentSize
= TotalSize
;
813 RemainingSize
= BufferSize
;
816 // Check if we can use the stack
818 if (BufferSize
<= MI_POOL_COPY_BYTES
)
823 PoolAddress
= (PVOID
)StackBuffer
;
830 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
831 if (!PoolAddress
) ASSERT(FALSE
);
832 HavePoolAddress
= TRUE
;
836 // Loop as long as there is still data
838 while (RemainingSize
> 0)
841 // Check if this transfer will finish everything off
843 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
846 // Attach to the source address space
848 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
851 // Reset state for this pass
853 FailedInMoving
= FALSE
;
854 ASSERT(FailedInProbe
== FALSE
);
857 // Protect user-mode copy
862 // If this is our first time, probe the buffer
864 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
867 // Catch a failure here
869 FailedInProbe
= TRUE
;
874 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
879 FailedInProbe
= FALSE
;
885 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
888 // Now let go of the source and grab to the target process
890 KeUnstackDetachProcess(&ApcState
);
891 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
894 // Check if this is our first time through
896 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
899 // Catch a failure here
901 FailedInProbe
= TRUE
;
906 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
911 FailedInProbe
= FALSE
;
915 // Now do the actual move
917 FailedInMoving
= TRUE
;
918 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
920 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
925 // Detach from whoever we may be attached to
927 KeUnstackDetachProcess(&ApcState
);
930 // Check if we had allocated pool
932 if (HavePoolAddress
) ExFreePool(PoolAddress
);
935 // Check if we failed during the probe
942 Status
= _SEH2_GetExceptionCode();
943 _SEH2_YIELD(return Status
);
947 // Otherwise, we failed, probably during the move
949 *ReturnSize
= BufferSize
- RemainingSize
;
953 // Check if we know exactly where we stopped copying
958 // Return the exact number of bytes copied
960 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
965 // Return partial copy
967 Status
= STATUS_PARTIAL_COPY
;
972 // Check for SEH status
974 if (Status
!= STATUS_SUCCESS
) return Status
;
977 // Detach from target
979 KeUnstackDetachProcess(&ApcState
);
982 // Update location and size
984 RemainingSize
-= CurrentSize
;
985 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
986 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
991 // Check if we had allocated pool
993 if (HavePoolAddress
) ExFreePool(PoolAddress
);
998 *ReturnSize
= BufferSize
;
999 return STATUS_SUCCESS
;
1004 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1005 IN PVOID SourceAddress
,
1006 IN PEPROCESS TargetProcess
,
1007 OUT PVOID TargetAddress
,
1008 IN SIZE_T BufferSize
,
1009 IN KPROCESSOR_MODE PreviousMode
,
1010 OUT PSIZE_T ReturnSize
)
1013 PEPROCESS Process
= SourceProcess
;
1016 // Don't accept zero-sized buffers
1018 if (!BufferSize
) return STATUS_SUCCESS
;
1021 // If we are copying from ourselves, lock the target instead
1023 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1026 // Acquire rundown protection
1028 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1033 return STATUS_PROCESS_IS_TERMINATING
;
1037 // See if we should use the pool copy
1039 if (BufferSize
> MI_POOL_COPY_BYTES
)
1044 Status
= MiDoMappedCopy(SourceProcess
,
1057 Status
= MiDoPoolCopy(SourceProcess
,
1069 ExReleaseRundownProtection(&Process
->RundownProtect
);
1075 MmFlushVirtualMemory(IN PEPROCESS Process
,
1076 IN OUT PVOID
*BaseAddress
,
1077 IN OUT PSIZE_T RegionSize
,
1078 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1086 return STATUS_SUCCESS
;
1091 MiGetPageProtection(IN PMMPTE PointerPte
)
1097 /* Copy this PTE's contents */
1098 TempPte
= *PointerPte
;
1100 /* Assure it's not totally zero */
1101 ASSERT(TempPte
.u
.Long
);
1103 /* Check for a special prototype format */
1104 if (TempPte
.u
.Soft
.Valid
== 0 &&
1105 TempPte
.u
.Soft
.Prototype
== 1)
1107 /* Unsupported now */
1112 /* In the easy case of transition or demand zero PTE just return its protection */
1113 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1115 /* If we get here, the PTE is valid, so look up the page in PFN database */
1116 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1118 if (!Pfn
->u3
.e1
.PrototypePte
)
1120 /* Return protection of the original pte */
1121 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1124 /* This is hardware PTE */
1128 return PAGE_NOACCESS
;
1133 MiQueryAddressState(IN PVOID Va
,
1135 IN PEPROCESS TargetProcess
,
1136 OUT PULONG ReturnedProtect
,
1143 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1144 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1145 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1146 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1148 /* Only normal VADs supported */
1149 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1151 /* Get the PDE and PTE for the address */
1152 PointerPde
= MiAddressToPde(Va
);
1153 PointerPte
= MiAddressToPte(Va
);
1155 /* Return the next range */
1156 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1158 /* Loop to make sure the PDE is valid */
1164 /* Is the PDE empty? */
1165 if (!PointerPde
->u
.Long
)
1167 /* No address in this range used yet, move to the next PDE range */
1168 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1172 /* The PDE is not empty, but is it faulted in? */
1173 if (!PointerPde
->u
.Hard
.Valid
)
1175 /* It isn't, go ahead and do the fault */
1176 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1180 /* Check if the PDE was faulted in, making the PTE readable */
1181 if (!LockChange
) ValidPte
= TRUE
;
1182 } while (LockChange
);
1184 /* Is it safe to try reading the PTE? */
1187 /* FIXME: watch out for large pages */
1189 /* Capture the PTE */
1190 TempPte
= *PointerPte
;
1193 /* The PTE is valid, so it's not zeroed out */
1194 DemandZeroPte
= FALSE
;
1196 /* Check if it's valid or has a valid protection mask */
1197 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1198 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1199 (TempPte
.u
.Hard
.Valid
== 1))
1201 /* This means it's committed */
1204 /* Get protection state of this page */
1205 Protect
= MiGetPageProtection(PointerPte
);
1209 /* Otherwise our defaults should hold */
1210 ASSERT(Protect
== 0);
1211 ASSERT(State
== MEM_RESERVE
);
1216 /* Check if this was a demand-zero PTE, since we need to find the state */
1219 /* Check if the VAD is for committed memory */
1220 if (Vad
->u
.VadFlags
.MemCommit
)
1222 /* This is committed memory */
1225 /* Convert the protection */
1226 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1230 /* Return the protection code */
1231 *ReturnedProtect
= Protect
;
1235 /* PUBLIC FUNCTIONS ***********************************************************/
1242 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1253 MmSecureVirtualMemory(IN PVOID Address
,
1257 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1266 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1268 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1271 /* SYSTEM CALLS ***************************************************************/
1275 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1276 IN PVOID BaseAddress
,
1278 IN SIZE_T NumberOfBytesToRead
,
1279 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1281 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1283 NTSTATUS Status
= STATUS_SUCCESS
;
1284 SIZE_T BytesRead
= 0;
1288 // Check if we came from user mode
1290 if (PreviousMode
!= KernelMode
)
1293 // Validate the read addresses
1295 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1296 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1297 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1298 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1301 // Don't allow to write into kernel space
1303 return STATUS_ACCESS_VIOLATION
;
1307 // Enter SEH for probe
1312 // Probe the output value
1314 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1316 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1319 // Get exception code
1321 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1327 // Don't do zero-byte transfers
1329 if (NumberOfBytesToRead
)
1332 // Reference the process
1334 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1340 if (NT_SUCCESS(Status
))
1345 Status
= MmCopyVirtualMemory(Process
,
1347 PsGetCurrentProcess(),
1349 NumberOfBytesToRead
,
1354 // Dereference the process
1356 ObDereferenceObject(Process
);
1361 // Check if the caller sent this parameter
1363 if (NumberOfBytesRead
)
1366 // Enter SEH to guard write
1371 // Return the number of bytes read
1373 *NumberOfBytesRead
= BytesRead
;
1375 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1389 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1390 IN PVOID BaseAddress
,
1392 IN SIZE_T NumberOfBytesToWrite
,
1393 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1395 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1397 NTSTATUS Status
= STATUS_SUCCESS
;
1398 SIZE_T BytesWritten
= 0;
1402 // Check if we came from user mode
1404 if (PreviousMode
!= KernelMode
)
1407 // Validate the read addresses
1409 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1410 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1411 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1412 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1415 // Don't allow to write into kernel space
1417 return STATUS_ACCESS_VIOLATION
;
1421 // Enter SEH for probe
1426 // Probe the output value
1428 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1433 // Get exception code
1435 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1441 // Don't do zero-byte transfers
1443 if (NumberOfBytesToWrite
)
1446 // Reference the process
1448 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1454 if (NT_SUCCESS(Status
))
1459 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1463 NumberOfBytesToWrite
,
1468 // Dereference the process
1470 ObDereferenceObject(Process
);
1475 // Check if the caller sent this parameter
1477 if (NumberOfBytesWritten
)
1480 // Enter SEH to guard write
1485 // Return the number of bytes written
1487 *NumberOfBytesWritten
= BytesWritten
;
1489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1503 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1504 IN OUT PVOID
*UnsafeBaseAddress
,
1505 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1506 IN ULONG NewAccessProtection
,
1507 OUT PULONG UnsafeOldAccessProtection
)
1510 ULONG OldAccessProtection
;
1512 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1513 PVOID BaseAddress
= NULL
;
1514 SIZE_T NumberOfBytesToProtect
= 0;
1515 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1517 BOOLEAN Attached
= FALSE
;
1518 KAPC_STATE ApcState
;
1522 // Check for valid protection flags
1524 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1525 if (Protection
!= PAGE_NOACCESS
&&
1526 Protection
!= PAGE_READONLY
&&
1527 Protection
!= PAGE_READWRITE
&&
1528 Protection
!= PAGE_WRITECOPY
&&
1529 Protection
!= PAGE_EXECUTE
&&
1530 Protection
!= PAGE_EXECUTE_READ
&&
1531 Protection
!= PAGE_EXECUTE_READWRITE
&&
1532 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1537 return STATUS_INVALID_PAGE_PROTECTION
;
1541 // Check if we came from user mode
1543 if (PreviousMode
!= KernelMode
)
1546 // Enter SEH for probing
1551 // Validate all outputs
1553 ProbeForWritePointer(UnsafeBaseAddress
);
1554 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1555 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1560 BaseAddress
= *UnsafeBaseAddress
;
1561 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1563 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1566 // Get exception code
1568 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1577 BaseAddress
= *UnsafeBaseAddress
;
1578 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1582 // Catch illegal base address
1584 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1587 // Catch illegal region size
1589 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1594 return STATUS_INVALID_PARAMETER_3
;
1598 // 0 is also illegal
1600 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1603 // Get a reference to the process
1605 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1606 PROCESS_VM_OPERATION
,
1611 if (!NT_SUCCESS(Status
)) return Status
;
1614 // Check if we should attach
1616 if (CurrentProcess
!= Process
)
1621 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1626 // Do the actual work
1628 Status
= MiProtectVirtualMemory(Process
,
1630 &NumberOfBytesToProtect
,
1631 NewAccessProtection
,
1632 &OldAccessProtection
);
1637 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1640 // Release reference
1642 ObDereferenceObject(Process
);
1645 // Enter SEH to return data
1650 // Return data to user
1652 *UnsafeOldAccessProtection
= OldAccessProtection
;
1653 *UnsafeBaseAddress
= BaseAddress
;
1654 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1669 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1670 IN OUT PVOID
*BaseAddress
,
1671 IN OUT PSIZE_T NumberOfBytesToLock
,
1675 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1677 BOOLEAN Attached
= FALSE
;
1678 KAPC_STATE ApcState
;
1679 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1680 PVOID CapturedBaseAddress
;
1681 SIZE_T CapturedBytesToLock
;
1687 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1690 // Invalid set of flags
1692 return STATUS_INVALID_PARAMETER
;
1696 // At least one flag must be specified
1698 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1703 return STATUS_INVALID_PARAMETER
;
1707 // Enter SEH for probing
1712 // Validate output data
1714 ProbeForWritePointer(BaseAddress
);
1715 ProbeForWriteSize_t(NumberOfBytesToLock
);
1720 CapturedBaseAddress
= *BaseAddress
;
1721 CapturedBytesToLock
= *NumberOfBytesToLock
;
1723 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1726 // Get exception code
1728 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1733 // Catch illegal base address
1735 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1738 // Catch illegal region size
1740 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1745 return STATUS_INVALID_PARAMETER
;
1749 // 0 is also illegal
1751 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1754 // Get a reference to the process
1756 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1757 PROCESS_VM_OPERATION
,
1762 if (!NT_SUCCESS(Status
)) return Status
;
1765 // Check if this is is system-mapped
1767 if (MapType
& MAP_SYSTEM
)
1770 // Check for required privilege
1772 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1775 // Fail: Don't have it
1777 ObDereferenceObject(Process
);
1778 return STATUS_PRIVILEGE_NOT_HELD
;
1783 // Check if we should attach
1785 if (CurrentProcess
!= Process
)
1790 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1802 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1805 // Release reference
1807 ObDereferenceObject(Process
);
1810 // Enter SEH to return data
1815 // Return data to user
1817 *BaseAddress
= CapturedBaseAddress
;
1818 *NumberOfBytesToLock
= 0;
1820 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1823 // Get exception code
1825 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1832 return STATUS_SUCCESS
;
1837 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1838 IN OUT PVOID
*BaseAddress
,
1839 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1843 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1845 BOOLEAN Attached
= FALSE
;
1846 KAPC_STATE ApcState
;
1847 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1848 PVOID CapturedBaseAddress
;
1849 SIZE_T CapturedBytesToUnlock
;
1855 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1858 // Invalid set of flags
1860 return STATUS_INVALID_PARAMETER
;
1864 // At least one flag must be specified
1866 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1871 return STATUS_INVALID_PARAMETER
;
1875 // Enter SEH for probing
1880 // Validate output data
1882 ProbeForWritePointer(BaseAddress
);
1883 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1888 CapturedBaseAddress
= *BaseAddress
;
1889 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1891 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1894 // Get exception code
1896 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1901 // Catch illegal base address
1903 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1906 // Catch illegal region size
1908 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1913 return STATUS_INVALID_PARAMETER
;
1917 // 0 is also illegal
1919 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1922 // Get a reference to the process
1924 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1925 PROCESS_VM_OPERATION
,
1930 if (!NT_SUCCESS(Status
)) return Status
;
1933 // Check if this is is system-mapped
1935 if (MapType
& MAP_SYSTEM
)
1938 // Check for required privilege
1940 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1943 // Fail: Don't have it
1945 ObDereferenceObject(Process
);
1946 return STATUS_PRIVILEGE_NOT_HELD
;
1951 // Check if we should attach
1953 if (CurrentProcess
!= Process
)
1958 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1970 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1973 // Release reference
1975 ObDereferenceObject(Process
);
1978 // Enter SEH to return data
1983 // Return data to user
1985 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1986 *NumberOfBytesToUnlock
= 0;
1988 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1991 // Get exception code
1993 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2000 return STATUS_SUCCESS
;
2005 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
2006 IN OUT PVOID
*BaseAddress
,
2007 IN OUT PSIZE_T NumberOfBytesToFlush
,
2008 OUT PIO_STATUS_BLOCK IoStatusBlock
)
2012 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2013 PVOID CapturedBaseAddress
;
2014 SIZE_T CapturedBytesToFlush
;
2015 IO_STATUS_BLOCK LocalStatusBlock
;
2019 // Check if we came from user mode
2021 if (PreviousMode
!= KernelMode
)
2024 // Enter SEH for probing
2029 // Validate all outputs
2031 ProbeForWritePointer(BaseAddress
);
2032 ProbeForWriteSize_t(NumberOfBytesToFlush
);
2033 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2038 CapturedBaseAddress
= *BaseAddress
;
2039 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2041 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2044 // Get exception code
2046 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2055 CapturedBaseAddress
= *BaseAddress
;
2056 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2060 // Catch illegal base address
2062 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2065 // Catch illegal region size
2067 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2072 return STATUS_INVALID_PARAMETER
;
2076 // Get a reference to the process
2078 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2079 PROCESS_VM_OPERATION
,
2084 if (!NT_SUCCESS(Status
)) return Status
;
2089 Status
= MmFlushVirtualMemory(Process
,
2090 &CapturedBaseAddress
,
2091 &CapturedBytesToFlush
,
2095 // Release reference
2097 ObDereferenceObject(Process
);
2100 // Enter SEH to return data
2105 // Return data to user
2107 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2108 *NumberOfBytesToFlush
= 0;
2109 *IoStatusBlock
= LocalStatusBlock
;
2111 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2127 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2129 IN PVOID BaseAddress
,
2130 IN SIZE_T RegionSize
,
2131 IN PVOID
*UserAddressArray
,
2132 OUT PULONG_PTR EntriesInUserAddressArray
,
2133 OUT PULONG Granularity
)
2138 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2139 ULONG_PTR CapturedEntryCount
;
2143 // Check if we came from user mode
2145 if (PreviousMode
!= KernelMode
)
2148 // Enter SEH for probing
2153 // Catch illegal base address
2155 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2158 // Catch illegal region size
2160 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2165 return STATUS_INVALID_PARAMETER_3
;
2169 // Validate all data
2171 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2172 ProbeForWriteUlong(Granularity
);
2177 CapturedEntryCount
= *EntriesInUserAddressArray
;
2180 // Must have a count
2182 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2185 // Can't be larger than the maximum
2187 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2192 return STATUS_INVALID_PARAMETER_5
;
2196 // Probe the actual array
2198 ProbeForWrite(UserAddressArray
,
2199 CapturedEntryCount
* sizeof(PVOID
),
2202 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2205 // Get exception code
2207 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2216 CapturedEntryCount
= *EntriesInUserAddressArray
;
2217 ASSERT(CapturedEntryCount
!= 0);
2221 // Check if this is a local request
2223 if (ProcessHandle
== NtCurrentProcess())
2226 // No need to reference the process
2228 Process
= PsGetCurrentProcess();
2233 // Reference the target
2235 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2236 PROCESS_VM_OPERATION
,
2241 if (!NT_SUCCESS(Status
)) return Status
;
2245 // Compute the last address and validate it
2247 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2248 if (BaseAddress
> EndAddress
)
2253 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2254 return STATUS_INVALID_PARAMETER_4
;
2263 // Dereference if needed
2265 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2268 // Enter SEH to return data
2273 // Return data to user
2275 *EntriesInUserAddressArray
= 0;
2276 *Granularity
= PAGE_SIZE
;
2278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2281 // Get exception code
2283 Status
= _SEH2_GetExceptionCode();
2290 return STATUS_SUCCESS
;
2298 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2299 IN PVOID BaseAddress
,
2300 IN SIZE_T RegionSize
)
2305 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2306 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2309 // Catch illegal base address
2311 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2314 // Catch illegal region size
2316 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2321 return STATUS_INVALID_PARAMETER_3
;
2325 // Check if this is a local request
2327 if (ProcessHandle
== NtCurrentProcess())
2330 // No need to reference the process
2332 Process
= PsGetCurrentProcess();
2337 // Reference the target
2339 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2340 PROCESS_VM_OPERATION
,
2345 if (!NT_SUCCESS(Status
)) return Status
;
2349 // Compute the last address and validate it
2351 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2352 if (BaseAddress
> EndAddress
)
2357 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2358 return STATUS_INVALID_PARAMETER_3
;
2367 // Dereference if needed
2369 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2374 return STATUS_SUCCESS
;
2379 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
2380 IN PVOID BaseAddress
,
2381 OUT PVOID MemoryInformation
,
2382 IN SIZE_T MemoryInformationLength
,
2383 OUT PSIZE_T ReturnLength
)
2385 PEPROCESS TargetProcess
;
2386 NTSTATUS Status
= STATUS_SUCCESS
;
2388 PVOID Address
, NextAddress
;
2389 BOOLEAN Found
= FALSE
;
2390 ULONG NewProtect
, NewState
;
2392 MEMORY_BASIC_INFORMATION MemoryInfo
;
2393 KAPC_STATE ApcState
;
2394 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2395 PMEMORY_AREA MemoryArea
;
2396 SIZE_T ResultLength
;
2398 /* Check for illegal addresses in user-space, or the shared memory area */
2399 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2400 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
2402 Address
= PAGE_ALIGN(BaseAddress
);
2404 /* Make up an info structure describing this range */
2405 MemoryInfo
.BaseAddress
= Address
;
2406 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
2407 MemoryInfo
.Type
= MEM_PRIVATE
;
2409 /* Special case for shared data */
2410 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
2412 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
2413 MemoryInfo
.State
= MEM_COMMIT
;
2414 MemoryInfo
.Protect
= PAGE_READONLY
;
2415 MemoryInfo
.RegionSize
= PAGE_SIZE
;
2419 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
2420 MemoryInfo
.State
= MEM_RESERVE
;
2421 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2422 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
2425 /* Return the data, NtQueryInformation already probed it*/
2426 if (PreviousMode
!= KernelMode
)
2430 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2431 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2433 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2435 Status
= _SEH2_GetExceptionCode();
2441 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2442 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2448 /* Check if this is for a local or remote process */
2449 if (ProcessHandle
== NtCurrentProcess())
2451 TargetProcess
= PsGetCurrentProcess();
2455 /* Reference the target process */
2456 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2457 PROCESS_QUERY_INFORMATION
,
2459 ExGetPreviousMode(),
2460 (PVOID
*)&TargetProcess
,
2462 if (!NT_SUCCESS(Status
)) return Status
;
2464 /* Attach to it now */
2465 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2469 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2470 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2472 /* Scan on the right */
2473 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2474 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2477 /* Check if this VAD covers the allocation range */
2478 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2479 (BaseVpn
<= Vad
->EndingVpn
))
2486 /* Check if this VAD is too high */
2487 if (BaseVpn
< Vad
->StartingVpn
)
2489 /* Stop if there is no left child */
2490 if (!Vad
->LeftChild
) break;
2492 /* Search on the left next */
2493 Vad
= Vad
->LeftChild
;
2497 /* Then this VAD is too low, keep searching on the right */
2498 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2500 /* Stop if there is no right child */
2501 if (!Vad
->RightChild
) break;
2503 /* Search on the right next */
2504 Vad
= Vad
->RightChild
;
2509 /* Was a VAD found? */
2512 Address
= PAGE_ALIGN(BaseAddress
);
2514 /* Calculate region size */
2517 if (Vad
->StartingVpn
>= BaseVpn
)
2519 /* Region size is the free space till the start of that VAD */
2520 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2524 /* Get the next VAD */
2525 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
2528 /* Region size is the free space till the start of that VAD */
2529 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2533 /* Maximum possible region size with that base address */
2534 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2540 /* Maximum possible region size with that base address */
2541 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2544 /* Check if we were attached */
2545 if (ProcessHandle
!= NtCurrentProcess())
2547 /* Detach and derefernece the process */
2548 KeUnstackDetachProcess(&ApcState
);
2549 ObDereferenceObject(TargetProcess
);
2552 /* Build the rest of the initial information block */
2553 MemoryInfo
.BaseAddress
= Address
;
2554 MemoryInfo
.AllocationBase
= NULL
;
2555 MemoryInfo
.AllocationProtect
= 0;
2556 MemoryInfo
.State
= MEM_FREE
;
2557 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2558 MemoryInfo
.Type
= 0;
2560 /* Return the data, NtQueryInformation already probed it*/
2561 if (PreviousMode
!= KernelMode
)
2565 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2566 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2568 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2570 Status
= _SEH2_GetExceptionCode();
2576 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2577 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2583 /* This must be a VM VAD */
2584 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2586 /* Lock the address space of the process */
2587 MmLockAddressSpace(&TargetProcess
->Vm
);
2589 /* Find the memory area the specified address belongs to */
2590 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
2591 ASSERT(MemoryArea
!= NULL
);
2593 /* Determine information dependent on the memory area type */
2594 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
2596 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
2597 ASSERT(NT_SUCCESS(Status
));
2601 /* Build the initial information block */
2602 Address
= PAGE_ALIGN(BaseAddress
);
2603 MemoryInfo
.BaseAddress
= Address
;
2604 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2605 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2606 MemoryInfo
.Type
= MEM_PRIVATE
;
2608 /* Find the largest chunk of memory which has the same state and protection mask */
2609 MemoryInfo
.State
= MiQueryAddressState(Address
,
2612 &MemoryInfo
.Protect
,
2614 Address
= NextAddress
;
2615 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2617 /* Keep going unless the state or protection mask changed */
2618 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2619 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2620 Address
= NextAddress
;
2623 /* Now that we know the last VA address, calculate the region size */
2624 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2627 /* Unlock the address space of the process */
2628 MmUnlockAddressSpace(&TargetProcess
->Vm
);
2630 /* Check if we were attached */
2631 if (ProcessHandle
!= NtCurrentProcess())
2633 /* Detach and derefernece the process */
2634 KeUnstackDetachProcess(&ApcState
);
2635 ObDereferenceObject(TargetProcess
);
2638 /* Return the data, NtQueryInformation already probed it*/
2639 if (PreviousMode
!= KernelMode
)
2643 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2644 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2646 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2648 Status
= _SEH2_GetExceptionCode();
2654 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2655 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2659 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2660 "State: %lx Type: %lx Size: %lx\n",
2661 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2662 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2663 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2670 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
2671 IN PVOID BaseAddress
,
2672 OUT PVOID MemoryInformation
,
2673 IN SIZE_T MemoryInformationLength
,
2674 OUT PSIZE_T ReturnLength
)
2678 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
2679 UNICODE_STRING ModuleFileName
;
2680 PMEMORY_SECTION_NAME SectionName
= NULL
;
2681 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2683 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2684 PROCESS_QUERY_INFORMATION
,
2690 if (!NT_SUCCESS(Status
))
2692 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
2696 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
2697 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
2699 if (NT_SUCCESS(Status
))
2701 SectionName
= MemoryInformation
;
2702 if (PreviousMode
!= KernelMode
)
2706 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2707 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2708 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2710 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2713 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2715 Status
= _SEH2_GetExceptionCode();
2721 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2722 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2723 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2725 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2729 ObDereferenceObject(Process
);
2735 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2736 IN PVOID BaseAddress
,
2737 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2738 OUT PVOID MemoryInformation
,
2739 IN SIZE_T MemoryInformationLength
,
2740 OUT PSIZE_T ReturnLength
)
2742 NTSTATUS Status
= STATUS_SUCCESS
;
2743 KPROCESSOR_MODE PreviousMode
;
2745 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2747 /* Bail out if the address is invalid */
2748 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2750 /* Probe return buffer */
2751 PreviousMode
= ExGetPreviousMode();
2752 if (PreviousMode
!= KernelMode
)
2756 ProbeForWrite(MemoryInformation
,
2757 MemoryInformationLength
,
2760 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
2762 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2764 Status
= _SEH2_GetExceptionCode();
2768 if (!NT_SUCCESS(Status
))
2774 switch(MemoryInformationClass
)
2776 case MemoryBasicInformation
:
2777 /* Validate the size information of the class */
2778 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2780 /* The size is invalid */
2781 return STATUS_INFO_LENGTH_MISMATCH
;
2783 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
2786 MemoryInformationLength
,
2790 case MemorySectionName
:
2791 /* Validate the size information of the class */
2792 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
2794 /* The size is invalid */
2795 return STATUS_INFO_LENGTH_MISMATCH
;
2797 Status
= MiQueryMemorySectionName(ProcessHandle
,
2800 MemoryInformationLength
,
2803 case MemoryWorkingSetList
:
2804 case MemoryBasicVlmInformation
:
2806 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
2819 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
2820 IN OUT PVOID
* UBaseAddress
,
2821 IN ULONG_PTR ZeroBits
,
2822 IN OUT PSIZE_T URegionSize
,
2823 IN ULONG AllocationType
,
2828 NTSTATUS Status
= STATUS_SUCCESS
;
2832 PMMADDRESS_NODE ParentNode
;
2833 ULONG_PTR StartVpn
, EndVpn
;
2834 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
2835 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2836 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
2837 KAPC_STATE ApcState
;
2838 ULONG ProtectionMask
;
2839 BOOLEAN Attached
= FALSE
;
2840 BoundaryAddressMultiple
.QuadPart
= 0;
2841 TABLE_SEARCH_RESULT Result
;
2845 /* Check for valid Zero bits */
2848 DPRINT1("Too many zero bits\n");
2849 return STATUS_INVALID_PARAMETER_3
;
2852 /* Check for valid Allocation Types */
2853 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
2854 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
2856 DPRINT1("Invalid Allocation Type\n");
2857 return STATUS_INVALID_PARAMETER_5
;
2860 /* Check for at least one of these Allocation Types to be set */
2861 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
2863 DPRINT1("No memory allocation base type\n");
2864 return STATUS_INVALID_PARAMETER_5
;
2867 /* MEM_RESET is an exclusive flag, make sure that is valid too */
2868 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
2870 DPRINT1("Invalid use of MEM_RESET\n");
2871 return STATUS_INVALID_PARAMETER_5
;
2874 /* Check if large pages are being used */
2875 if (AllocationType
& MEM_LARGE_PAGES
)
2877 /* Large page allocations MUST be committed */
2878 if (!(AllocationType
& MEM_COMMIT
))
2880 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
2881 return STATUS_INVALID_PARAMETER_5
;
2884 /* These flags are not allowed with large page allocations */
2885 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
2887 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
2888 return STATUS_INVALID_PARAMETER_5
;
2892 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
2893 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
2895 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2896 return STATUS_INVALID_PARAMETER_5
;
2899 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
2900 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
2902 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2903 return STATUS_INVALID_PARAMETER_5
;
2906 /* Check for valid MEM_PHYSICAL usage */
2907 if (AllocationType
& MEM_PHYSICAL
)
2909 /* Only these flags are allowed with MEM_PHYSIAL */
2910 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
2912 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
2913 return STATUS_INVALID_PARAMETER_5
;
2916 /* Then make sure PAGE_READWRITE is used */
2917 if (Protect
!= PAGE_READWRITE
)
2919 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
2920 return STATUS_INVALID_PARAMETER_6
;
2924 /* Calculate the protection mask and make sure it's valid */
2925 ProtectionMask
= MiMakeProtectionMask(Protect
);
2926 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2928 DPRINT1("Invalid protection mask\n");
2929 return STATUS_INVALID_PAGE_PROTECTION
;
2935 /* Check for user-mode parameters */
2936 if (PreviousMode
!= KernelMode
)
2938 /* Make sure they are writable */
2939 ProbeForWritePointer(UBaseAddress
);
2940 ProbeForWriteUlong(URegionSize
);
2943 /* Capture their values */
2944 BaseAddress
= *UBaseAddress
;
2945 RegionSize
= *URegionSize
;
2947 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2949 /* Return the exception code */
2950 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2954 /* Make sure there's a size specified */
2957 DPRINT1("Region size is invalid (zero)\n");
2958 return STATUS_INVALID_PARAMETER_4
;
2961 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)BaseAddress
+ RegionSize
) -
2962 PAGE_ROUND_DOWN(BaseAddress
);
2963 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(BaseAddress
);
2964 StartVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2965 EndVpn
= ((ULONG_PTR
)BaseAddress
+ RegionSize
- 1) >> PAGE_SHIFT
;
2967 /* Make sure the allocation isn't past the VAD area */
2968 if (BaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
2970 DPRINT1("Virtual allocation base above User Space\n");
2971 return STATUS_INVALID_PARAMETER_2
;
2974 /* Make sure the allocation wouldn't overflow past the VAD area */
2975 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)BaseAddress
) < RegionSize
)
2977 DPRINT1("Region size would overflow into kernel-memory\n");
2978 return STATUS_INVALID_PARAMETER_4
;
2981 /* Check if this is for the current process */
2982 if (ProcessHandle
== NtCurrentProcess())
2984 /* We already have the current process, no need to go through Ob */
2985 Process
= CurrentProcess
;
2989 /* Reference the handle for correct permissions */
2990 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2991 PROCESS_VM_OPERATION
,
2996 if (!NT_SUCCESS(Status
)) return Status
;
2998 /* Check if not running in the current process */
2999 if (CurrentProcess
!= Process
)
3002 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3007 /* Check for large page allocations */
3008 if (AllocationType
& MEM_LARGE_PAGES
)
3010 /* The lock memory privilege is required */
3011 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3013 /* Fail without it */
3014 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3015 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3016 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3017 return STATUS_PRIVILEGE_NOT_HELD
;
3023 * Copy on Write is reserved for system use. This case is a certain failure
3024 * but there may be other cases...needs more testing
3026 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
3027 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
3029 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
3030 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3031 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3032 return STATUS_INVALID_PAGE_PROTECTION
;
3035 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
3036 DPRINT("Type %x\n", Type
);
3038 /* Lock the process address space */
3039 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
3041 if(BaseAddress
!= 0)
3044 * An address was provided. Let's see if we've already
3047 if(MiCheckForConflictingNode(StartVpn
, EndVpn
, &Process
->VadRoot
) != NULL
)
3049 /* Can't reserve twice the same range */
3050 if(AllocationType
& MEM_RESERVE
)
3052 Status
= STATUS_CONFLICTING_ADDRESSES
;
3053 DPRINT1("Trying to reserve twice the same range.\n");
3056 /* Great there's already something there. What shall we do ? */
3057 if(AllocationType
== MEM_RESET
)
3060 /* Reset the dirty bits for each PTEs */
3065 ASSERT(AllocationType
& MEM_COMMIT
);
3067 /* Mark the VAD as committed */
3072 /* There's nothing */
3073 if(!(AllocationType
& MEM_RESERVE
))
3075 Status
= STATUS_ACCESS_DENIED
;
3079 /* Now we can reserve our chunk of memory */
3083 /* No base address was given. */
3084 if(!(AllocationType
& MEM_RESERVE
))
3086 DPRINT1("Providing NULL base address witout MEM_RESERVE.\n");
3088 Status
= STATUS_INVALID_PARAMETER_5
;
3092 /* Find an empty range in Address Space */
3093 if(AllocationType
& MEM_TOP_DOWN
)
3095 /* Top down allocation */
3096 Result
= MiFindEmptyAddressRangeDownTree(RegionSize
,
3097 (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
,
3098 (ZeroBits
> PAGE_SHIFT
) ? 1 << ZeroBits
: PAGE_SIZE
,
3100 (PULONG_PTR
)&BaseAddress
,
3103 if(Result
== TableFoundNode
)
3105 /* This means failure */
3106 Status
= STATUS_NO_MEMORY
;
3112 /* Good old bottom up allocation */
3113 Status
= MiFindEmptyAddressRangeInTree(RegionSize
,
3114 (ZeroBits
> PAGE_SHIFT
) ? 1 << ZeroBits
: PAGE_SIZE
,
3117 (PULONG_PTR
)&BaseAddress
);
3118 if(!NT_SUCCESS(Status
))
3124 StartVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
3125 EndVpn
= ((ULONG_PTR
)BaseAddress
+ RegionSize
- 1) >> PAGE_SHIFT
;
3129 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD
), TAG_MVAD
);
3132 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3135 RtlZeroMemory(Vad
, sizeof(MMVAD
));
3138 Vad
->StartingVpn
= StartVpn
;
3139 Vad
->EndingVpn
= EndVpn
;
3140 /* Set protection */
3141 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3142 /* Should it be already marked as committed ? */
3143 if(AllocationType
& MEM_COMMIT
)
3144 Vad
->u
.VadFlags
.MemCommit
= 1;
3145 if(AllocationType
& MEM_PHYSICAL
)
3148 Vad
->u
.VadFlags
.VadType
= VadAwe
;
3151 MiLockProcessWorkingSet(Process
, PsGetCurrentThread());
3152 MiInsertVad(Vad
, Process
);
3153 MiUnlockProcessWorkingSet(Process
, PsGetCurrentThread());
3157 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
3158 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3159 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3161 *UBaseAddress
= BaseAddress
;
3162 *URegionSize
= RegionSize
;
3163 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);