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 share count */
262 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
264 /* Either a fork, or this is the shared user data page */
265 if (PointerPte
<= MiHighestUserPte
)
267 /* If it's not the shared user page, then crash, since there's no fork() yet */
268 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
269 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
271 /* Must be some sort of memory corruption */
272 KeBugCheckEx(MEMORY_MANAGEMENT
,
274 (ULONG_PTR
)PointerPte
,
275 (ULONG_PTR
)PrototypePte
,
276 (ULONG_PTR
)Pfn1
->PteAddress
);
282 /* Make sure the saved PTE address is valid */
283 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
285 /* The PFN entry is illegal, or invalid */
286 KeBugCheckEx(MEMORY_MANAGEMENT
,
288 (ULONG_PTR
)PointerPte
,
290 (ULONG_PTR
)Pfn1
->PteAddress
);
293 /* There should only be 1 shared reference count */
294 ASSERT(Pfn1
->u2
.ShareCount
== 1);
296 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
297 //DPRINT1("Dropping a ref...\n");
298 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
300 /* Mark the PFN for deletion and dereference what should be the last ref */
301 MI_SET_PFN_DELETED(Pfn1
);
302 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
304 /* We should eventually do this */
305 //CurrentProcess->NumberOfPrivatePages--;
308 /* Destroy the PTE and flush the TLB */
309 PointerPte
->u
.Long
= 0;
315 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
316 IN ULONG_PTR EndingAddress
,
319 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
322 PEPROCESS CurrentProcess
;
324 BOOLEAN AddressGap
= FALSE
;
325 PSUBSECTION Subsection
;
326 PUSHORT UsedPageTableEntries
;
328 /* Get out if this is a fake VAD, RosMm will free the marea pages */
329 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
331 /* Grab the process and PTE/PDE for the address being deleted */
332 CurrentProcess
= PsGetCurrentProcess();
333 PointerPde
= MiAddressToPde(Va
);
334 PointerPte
= MiAddressToPte(Va
);
336 /* Check if this is a section VAD or a VM VAD */
337 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
339 /* Don't worry about prototypes */
340 PrototypePte
= LastPrototypePte
= NULL
;
344 /* Get the prototype PTE */
345 PrototypePte
= Vad
->FirstPrototypePte
;
346 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
349 /* In all cases, we don't support fork() yet */
350 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
352 /* Loop the PTE for each VA */
355 /* First keep going until we find a valid PDE */
356 while (!PointerPde
->u
.Long
)
358 /* There are gaps in the address space */
361 /* Still no valid PDE, try the next 4MB (or whatever) */
364 /* Update the PTE on this new boundary */
365 PointerPte
= MiPteToAddress(PointerPde
);
367 /* Check if all the PDEs are invalid, so there's nothing to free */
368 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
369 if (Va
> EndingAddress
) return;
372 /* Now check if the PDE is mapped in */
373 if (!PointerPde
->u
.Hard
.Valid
)
375 /* It isn't, so map it in */
376 PointerPte
= MiPteToAddress(PointerPde
);
377 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
380 /* Now we should have a valid PDE, mapped in, and still have some VA */
381 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
382 ASSERT(Va
<= EndingAddress
);
383 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
385 /* Check if this is a section VAD with gaps in it */
386 if ((AddressGap
) && (LastPrototypePte
))
388 /* We need to skip to the next correct prototype PTE */
389 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
391 /* And we need the subsection to skip to the next last prototype PTE */
392 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
396 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
400 /* No more subsections, we are done with prototype PTEs */
405 /* Lock the PFN Database while we delete the PTEs */
406 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
409 /* Capture the PDE and make sure it exists */
410 TempPte
= *PointerPte
;
413 DPRINT("Decrement used PTEs by address: %lx\n", Va
);
414 (*UsedPageTableEntries
)--;
415 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
416 DPRINT("Refs: %lx\n", (*UsedPageTableEntries
));
418 /* Check if the PTE is actually mapped in */
419 if (TempPte
.u
.Long
& 0xFFFFFC01)
421 /* Are we dealing with section VAD? */
422 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
424 /* We need to skip to the next correct prototype PTE */
425 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
427 /* And we need the subsection to skip to the next last prototype PTE */
428 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
432 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
436 /* No more subsections, we are done with prototype PTEs */
441 /* Check for prototype PTE */
442 if ((TempPte
.u
.Hard
.Valid
== 0) &&
443 (TempPte
.u
.Soft
.Prototype
== 1))
446 PointerPte
->u
.Long
= 0;
450 /* Delete the PTE proper */
451 MiDeletePte(PointerPte
,
459 /* The PTE was never mapped, just nuke it here */
460 PointerPte
->u
.Long
= 0;
464 /* Update the address and PTE for it */
469 /* Making sure the PDE is still valid */
470 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
472 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
474 /* The PDE should still be valid at this point */
475 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
477 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va
, PointerPde
->u
.Hard
.PageFrameNumber
);
478 if (!(*UsedPageTableEntries
))
480 DPRINT("They are!\n");
481 if (PointerPde
->u
.Long
!= 0)
483 DPRINT("PDE active: %lx in %16s\n", PointerPde
->u
.Hard
.PageFrameNumber
, CurrentProcess
->ImageFileName
);
485 /* Delete the PTE proper */
486 MiDeletePte(PointerPde
,
487 MiPteToAddress(PointerPde
),
493 /* Release the lock and get out if we're done */
494 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
495 if (Va
> EndingAddress
) return;
497 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
498 PointerPde
= MiAddressToPde(Va
);
504 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
505 OUT PBOOLEAN HaveBadAddress
,
506 OUT PULONG_PTR BadAddress
)
508 PEXCEPTION_RECORD ExceptionRecord
;
514 *HaveBadAddress
= FALSE
;
517 // Get the exception record
519 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
522 // Look at the exception code
524 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
525 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
526 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
529 // We can tell the address if we have more than one parameter
531 if (ExceptionRecord
->NumberParameters
> 1)
534 // Return the address
536 *HaveBadAddress
= TRUE
;
537 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
542 // Continue executing the next handler
544 return EXCEPTION_EXECUTE_HANDLER
;
549 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
550 IN PVOID SourceAddress
,
551 IN PEPROCESS TargetProcess
,
552 OUT PVOID TargetAddress
,
553 IN SIZE_T BufferSize
,
554 IN KPROCESSOR_MODE PreviousMode
,
555 OUT PSIZE_T ReturnSize
)
557 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
558 PMDL Mdl
= (PMDL
)MdlBuffer
;
559 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
560 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
561 volatile BOOLEAN PagesLocked
;
562 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
563 volatile PVOID MdlAddress
;
565 BOOLEAN HaveBadAddress
;
566 ULONG_PTR BadAddress
;
567 NTSTATUS Status
= STATUS_SUCCESS
;
571 // Calculate the maximum amount of data to move
573 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
574 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
575 CurrentSize
= TotalSize
;
576 RemainingSize
= BufferSize
;
579 // Loop as long as there is still data
581 while (RemainingSize
> 0)
584 // Check if this transfer will finish everything off
586 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
589 // Attach to the source address space
591 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
594 // Reset state for this pass
598 FailedInMoving
= FALSE
;
599 ASSERT(FailedInProbe
== FALSE
);
602 // Protect user-mode copy
607 // If this is our first time, probe the buffer
609 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
612 // Catch a failure here
614 FailedInProbe
= TRUE
;
619 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
624 FailedInProbe
= FALSE
;
628 // Initialize and probe and lock the MDL
630 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
631 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
637 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
646 // Use our SEH handler to pick this up
648 FailedInMapping
= TRUE
;
649 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
653 // Now let go of the source and grab to the target process
655 KeUnstackDetachProcess(&ApcState
);
656 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
659 // Check if this is our first time through
661 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
664 // Catch a failure here
666 FailedInProbe
= TRUE
;
671 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
676 FailedInProbe
= FALSE
;
680 // Now do the actual move
682 FailedInMoving
= TRUE
;
683 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
685 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
690 // Detach from whoever we may be attached to
692 KeUnstackDetachProcess(&ApcState
);
695 // Check if we had mapped the pages
697 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
700 // Check if we had locked the pages
702 if (PagesLocked
) MmUnlockPages(Mdl
);
705 // Check if we hit working set quota
707 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
712 return STATUS_WORKING_SET_QUOTA
;
716 // Check if we failed during the probe or mapping
718 if ((FailedInProbe
) || (FailedInMapping
))
723 Status
= _SEH2_GetExceptionCode();
724 _SEH2_YIELD(return Status
);
728 // Otherwise, we failed probably during the move
730 *ReturnSize
= BufferSize
- RemainingSize
;
734 // Check if we know exactly where we stopped copying
739 // Return the exact number of bytes copied
741 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
746 // Return partial copy
748 Status
= STATUS_PARTIAL_COPY
;
753 // Check for SEH status
755 if (Status
!= STATUS_SUCCESS
) return Status
;
758 // Detach from target
760 KeUnstackDetachProcess(&ApcState
);
765 MmUnmapLockedPages(MdlAddress
, Mdl
);
769 // Update location and size
771 RemainingSize
-= CurrentSize
;
772 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
773 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
779 *ReturnSize
= BufferSize
;
780 return STATUS_SUCCESS
;
785 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
786 IN PVOID SourceAddress
,
787 IN PEPROCESS TargetProcess
,
788 OUT PVOID TargetAddress
,
789 IN SIZE_T BufferSize
,
790 IN KPROCESSOR_MODE PreviousMode
,
791 OUT PSIZE_T ReturnSize
)
793 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
794 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
795 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
796 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
799 BOOLEAN HaveBadAddress
;
800 ULONG_PTR BadAddress
;
801 NTSTATUS Status
= STATUS_SUCCESS
;
805 // Calculate the maximum amount of data to move
807 TotalSize
= MI_MAX_TRANSFER_SIZE
;
808 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
809 CurrentSize
= TotalSize
;
810 RemainingSize
= BufferSize
;
813 // Check if we can use the stack
815 if (BufferSize
<= MI_POOL_COPY_BYTES
)
820 PoolAddress
= (PVOID
)StackBuffer
;
827 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
828 if (!PoolAddress
) ASSERT(FALSE
);
829 HavePoolAddress
= TRUE
;
833 // Loop as long as there is still data
835 while (RemainingSize
> 0)
838 // Check if this transfer will finish everything off
840 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
843 // Attach to the source address space
845 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
848 // Reset state for this pass
850 FailedInMoving
= FALSE
;
851 ASSERT(FailedInProbe
== FALSE
);
854 // Protect user-mode copy
859 // If this is our first time, probe the buffer
861 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
864 // Catch a failure here
866 FailedInProbe
= TRUE
;
871 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
876 FailedInProbe
= FALSE
;
882 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
885 // Now let go of the source and grab to the target process
887 KeUnstackDetachProcess(&ApcState
);
888 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
891 // Check if this is our first time through
893 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
896 // Catch a failure here
898 FailedInProbe
= TRUE
;
903 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
908 FailedInProbe
= FALSE
;
912 // Now do the actual move
914 FailedInMoving
= TRUE
;
915 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
917 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
922 // Detach from whoever we may be attached to
924 KeUnstackDetachProcess(&ApcState
);
927 // Check if we had allocated pool
929 if (HavePoolAddress
) ExFreePool(PoolAddress
);
932 // Check if we failed during the probe
939 Status
= _SEH2_GetExceptionCode();
940 _SEH2_YIELD(return Status
);
944 // Otherwise, we failed, probably during the move
946 *ReturnSize
= BufferSize
- RemainingSize
;
950 // Check if we know exactly where we stopped copying
955 // Return the exact number of bytes copied
957 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
962 // Return partial copy
964 Status
= STATUS_PARTIAL_COPY
;
969 // Check for SEH status
971 if (Status
!= STATUS_SUCCESS
) return Status
;
974 // Detach from target
976 KeUnstackDetachProcess(&ApcState
);
979 // Update location and size
981 RemainingSize
-= CurrentSize
;
982 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
983 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
988 // Check if we had allocated pool
990 if (HavePoolAddress
) ExFreePool(PoolAddress
);
995 *ReturnSize
= BufferSize
;
996 return STATUS_SUCCESS
;
1001 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1002 IN PVOID SourceAddress
,
1003 IN PEPROCESS TargetProcess
,
1004 OUT PVOID TargetAddress
,
1005 IN SIZE_T BufferSize
,
1006 IN KPROCESSOR_MODE PreviousMode
,
1007 OUT PSIZE_T ReturnSize
)
1010 PEPROCESS Process
= SourceProcess
;
1013 // Don't accept zero-sized buffers
1015 if (!BufferSize
) return STATUS_SUCCESS
;
1018 // If we are copying from ourselves, lock the target instead
1020 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1023 // Acquire rundown protection
1025 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1030 return STATUS_PROCESS_IS_TERMINATING
;
1034 // See if we should use the pool copy
1036 if (BufferSize
> MI_POOL_COPY_BYTES
)
1041 Status
= MiDoMappedCopy(SourceProcess
,
1054 Status
= MiDoPoolCopy(SourceProcess
,
1066 ExReleaseRundownProtection(&Process
->RundownProtect
);
1072 MmFlushVirtualMemory(IN PEPROCESS Process
,
1073 IN OUT PVOID
*BaseAddress
,
1074 IN OUT PSIZE_T RegionSize
,
1075 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1083 return STATUS_SUCCESS
;
1088 MiGetPageProtection(IN PMMPTE PointerPte
)
1094 /* Copy this PTE's contents */
1095 TempPte
= *PointerPte
;
1097 /* Assure it's not totally zero */
1098 ASSERT(TempPte
.u
.Long
);
1100 /* Check for a special prototype format */
1101 if (TempPte
.u
.Soft
.Valid
== 0 &&
1102 TempPte
.u
.Soft
.Prototype
== 1)
1104 /* Unsupported now */
1109 /* In the easy case of transition or demand zero PTE just return its protection */
1110 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1112 /* If we get here, the PTE is valid, so look up the page in PFN database */
1113 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1115 if (!Pfn
->u3
.e1
.PrototypePte
)
1117 /* Return protection of the original pte */
1118 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1121 /* This is hardware PTE */
1125 return PAGE_NOACCESS
;
1130 MiQueryAddressState(IN PVOID Va
,
1132 IN PEPROCESS TargetProcess
,
1133 OUT PULONG ReturnedProtect
,
1140 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1141 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1142 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1143 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1145 /* Only normal VADs supported */
1146 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1148 /* Get the PDE and PTE for the address */
1149 PointerPde
= MiAddressToPde(Va
);
1150 PointerPte
= MiAddressToPte(Va
);
1152 /* Return the next range */
1153 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1155 /* Loop to make sure the PDE is valid */
1161 /* Is the PDE empty? */
1162 if (!PointerPde
->u
.Long
)
1164 /* No address in this range used yet, move to the next PDE range */
1165 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1169 /* The PDE is not empty, but is it faulted in? */
1170 if (!PointerPde
->u
.Hard
.Valid
)
1172 /* It isn't, go ahead and do the fault */
1173 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1177 /* Check if the PDE was faulted in, making the PTE readable */
1178 if (!LockChange
) ValidPte
= TRUE
;
1179 } while (LockChange
);
1181 /* Is it safe to try reading the PTE? */
1184 /* FIXME: watch out for large pages */
1186 /* Capture the PTE */
1187 TempPte
= *PointerPte
;
1190 /* The PTE is valid, so it's not zeroed out */
1191 DemandZeroPte
= FALSE
;
1193 /* Check if it's valid or has a valid protection mask */
1194 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1195 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1196 (TempPte
.u
.Hard
.Valid
== 1))
1198 /* This means it's committed */
1201 /* Get protection state of this page */
1202 Protect
= MiGetPageProtection(PointerPte
);
1206 /* Otherwise our defaults should hold */
1207 ASSERT(Protect
== 0);
1208 ASSERT(State
== MEM_RESERVE
);
1213 /* Check if this was a demand-zero PTE, since we need to find the state */
1216 /* Check if the VAD is for committed memory */
1217 if (Vad
->u
.VadFlags
.MemCommit
)
1219 /* This is committed memory */
1222 /* Convert the protection */
1223 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1227 /* Return the protection code */
1228 *ReturnedProtect
= Protect
;
1232 /* PUBLIC FUNCTIONS ***********************************************************/
1239 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
1250 MmSecureVirtualMemory(IN PVOID Address
,
1254 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1263 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
1265 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
1268 /* SYSTEM CALLS ***************************************************************/
1272 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1273 IN PVOID BaseAddress
,
1275 IN SIZE_T NumberOfBytesToRead
,
1276 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
1278 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1280 NTSTATUS Status
= STATUS_SUCCESS
;
1281 SIZE_T BytesRead
= 0;
1285 // Check if we came from user mode
1287 if (PreviousMode
!= KernelMode
)
1290 // Validate the read addresses
1292 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
1293 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
1294 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
1295 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
1298 // Don't allow to write into kernel space
1300 return STATUS_ACCESS_VIOLATION
;
1304 // Enter SEH for probe
1309 // Probe the output value
1311 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
1313 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1316 // Get exception code
1318 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1324 // Don't do zero-byte transfers
1326 if (NumberOfBytesToRead
)
1329 // Reference the process
1331 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1337 if (NT_SUCCESS(Status
))
1342 Status
= MmCopyVirtualMemory(Process
,
1344 PsGetCurrentProcess(),
1346 NumberOfBytesToRead
,
1351 // Dereference the process
1353 ObDereferenceObject(Process
);
1358 // Check if the caller sent this parameter
1360 if (NumberOfBytesRead
)
1363 // Enter SEH to guard write
1368 // Return the number of bytes read
1370 *NumberOfBytesRead
= BytesRead
;
1372 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1386 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1387 IN PVOID BaseAddress
,
1389 IN SIZE_T NumberOfBytesToWrite
,
1390 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
1392 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1394 NTSTATUS Status
= STATUS_SUCCESS
;
1395 SIZE_T BytesWritten
= 0;
1399 // Check if we came from user mode
1401 if (PreviousMode
!= KernelMode
)
1404 // Validate the read addresses
1406 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
1407 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
1408 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
1409 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
1412 // Don't allow to write into kernel space
1414 return STATUS_ACCESS_VIOLATION
;
1418 // Enter SEH for probe
1423 // Probe the output value
1425 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
1427 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1430 // Get exception code
1432 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1438 // Don't do zero-byte transfers
1440 if (NumberOfBytesToWrite
)
1443 // Reference the process
1445 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1451 if (NT_SUCCESS(Status
))
1456 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
1460 NumberOfBytesToWrite
,
1465 // Dereference the process
1467 ObDereferenceObject(Process
);
1472 // Check if the caller sent this parameter
1474 if (NumberOfBytesWritten
)
1477 // Enter SEH to guard write
1482 // Return the number of bytes written
1484 *NumberOfBytesWritten
= BytesWritten
;
1486 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1500 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1501 IN OUT PVOID
*UnsafeBaseAddress
,
1502 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
1503 IN ULONG NewAccessProtection
,
1504 OUT PULONG UnsafeOldAccessProtection
)
1507 ULONG OldAccessProtection
;
1509 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1510 PVOID BaseAddress
= NULL
;
1511 SIZE_T NumberOfBytesToProtect
= 0;
1512 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1514 BOOLEAN Attached
= FALSE
;
1515 KAPC_STATE ApcState
;
1519 // Check for valid protection flags
1521 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1522 if (Protection
!= PAGE_NOACCESS
&&
1523 Protection
!= PAGE_READONLY
&&
1524 Protection
!= PAGE_READWRITE
&&
1525 Protection
!= PAGE_WRITECOPY
&&
1526 Protection
!= PAGE_EXECUTE
&&
1527 Protection
!= PAGE_EXECUTE_READ
&&
1528 Protection
!= PAGE_EXECUTE_READWRITE
&&
1529 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1534 return STATUS_INVALID_PAGE_PROTECTION
;
1538 // Check if we came from user mode
1540 if (PreviousMode
!= KernelMode
)
1543 // Enter SEH for probing
1548 // Validate all outputs
1550 ProbeForWritePointer(UnsafeBaseAddress
);
1551 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1552 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1557 BaseAddress
= *UnsafeBaseAddress
;
1558 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1560 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1563 // Get exception code
1565 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1574 BaseAddress
= *UnsafeBaseAddress
;
1575 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1579 // Catch illegal base address
1581 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1584 // Catch illegal region size
1586 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1591 return STATUS_INVALID_PARAMETER_3
;
1595 // 0 is also illegal
1597 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1600 // Get a reference to the process
1602 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1603 PROCESS_VM_OPERATION
,
1608 if (!NT_SUCCESS(Status
)) return Status
;
1611 // Check if we should attach
1613 if (CurrentProcess
!= Process
)
1618 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1623 // Do the actual work
1625 Status
= MiProtectVirtualMemory(Process
,
1627 &NumberOfBytesToProtect
,
1628 NewAccessProtection
,
1629 &OldAccessProtection
);
1634 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1637 // Release reference
1639 ObDereferenceObject(Process
);
1642 // Enter SEH to return data
1647 // Return data to user
1649 *UnsafeOldAccessProtection
= OldAccessProtection
;
1650 *UnsafeBaseAddress
= BaseAddress
;
1651 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1653 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1666 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1667 IN OUT PVOID
*BaseAddress
,
1668 IN OUT PSIZE_T NumberOfBytesToLock
,
1672 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1674 BOOLEAN Attached
= FALSE
;
1675 KAPC_STATE ApcState
;
1676 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1677 PVOID CapturedBaseAddress
;
1678 SIZE_T CapturedBytesToLock
;
1684 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1687 // Invalid set of flags
1689 return STATUS_INVALID_PARAMETER
;
1693 // At least one flag must be specified
1695 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1700 return STATUS_INVALID_PARAMETER
;
1704 // Enter SEH for probing
1709 // Validate output data
1711 ProbeForWritePointer(BaseAddress
);
1712 ProbeForWriteSize_t(NumberOfBytesToLock
);
1717 CapturedBaseAddress
= *BaseAddress
;
1718 CapturedBytesToLock
= *NumberOfBytesToLock
;
1720 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1723 // Get exception code
1725 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1730 // Catch illegal base address
1732 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1735 // Catch illegal region size
1737 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1742 return STATUS_INVALID_PARAMETER
;
1746 // 0 is also illegal
1748 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1751 // Get a reference to the process
1753 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1754 PROCESS_VM_OPERATION
,
1759 if (!NT_SUCCESS(Status
)) return Status
;
1762 // Check if this is is system-mapped
1764 if (MapType
& MAP_SYSTEM
)
1767 // Check for required privilege
1769 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1772 // Fail: Don't have it
1774 ObDereferenceObject(Process
);
1775 return STATUS_PRIVILEGE_NOT_HELD
;
1780 // Check if we should attach
1782 if (CurrentProcess
!= Process
)
1787 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1799 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1802 // Release reference
1804 ObDereferenceObject(Process
);
1807 // Enter SEH to return data
1812 // Return data to user
1814 *BaseAddress
= CapturedBaseAddress
;
1815 *NumberOfBytesToLock
= 0;
1817 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1820 // Get exception code
1822 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1829 return STATUS_SUCCESS
;
1834 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1835 IN OUT PVOID
*BaseAddress
,
1836 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1840 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1842 BOOLEAN Attached
= FALSE
;
1843 KAPC_STATE ApcState
;
1844 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1845 PVOID CapturedBaseAddress
;
1846 SIZE_T CapturedBytesToUnlock
;
1852 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1855 // Invalid set of flags
1857 return STATUS_INVALID_PARAMETER
;
1861 // At least one flag must be specified
1863 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1868 return STATUS_INVALID_PARAMETER
;
1872 // Enter SEH for probing
1877 // Validate output data
1879 ProbeForWritePointer(BaseAddress
);
1880 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1885 CapturedBaseAddress
= *BaseAddress
;
1886 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1888 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1891 // Get exception code
1893 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1898 // Catch illegal base address
1900 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1903 // Catch illegal region size
1905 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1910 return STATUS_INVALID_PARAMETER
;
1914 // 0 is also illegal
1916 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1919 // Get a reference to the process
1921 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1922 PROCESS_VM_OPERATION
,
1927 if (!NT_SUCCESS(Status
)) return Status
;
1930 // Check if this is is system-mapped
1932 if (MapType
& MAP_SYSTEM
)
1935 // Check for required privilege
1937 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1940 // Fail: Don't have it
1942 ObDereferenceObject(Process
);
1943 return STATUS_PRIVILEGE_NOT_HELD
;
1948 // Check if we should attach
1950 if (CurrentProcess
!= Process
)
1955 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1967 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1970 // Release reference
1972 ObDereferenceObject(Process
);
1975 // Enter SEH to return data
1980 // Return data to user
1982 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1983 *NumberOfBytesToUnlock
= 0;
1985 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1988 // Get exception code
1990 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1997 return STATUS_SUCCESS
;
2002 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
2003 IN OUT PVOID
*BaseAddress
,
2004 IN OUT PSIZE_T NumberOfBytesToFlush
,
2005 OUT PIO_STATUS_BLOCK IoStatusBlock
)
2009 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2010 PVOID CapturedBaseAddress
;
2011 SIZE_T CapturedBytesToFlush
;
2012 IO_STATUS_BLOCK LocalStatusBlock
;
2016 // Check if we came from user mode
2018 if (PreviousMode
!= KernelMode
)
2021 // Enter SEH for probing
2026 // Validate all outputs
2028 ProbeForWritePointer(BaseAddress
);
2029 ProbeForWriteSize_t(NumberOfBytesToFlush
);
2030 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2035 CapturedBaseAddress
= *BaseAddress
;
2036 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2038 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2041 // Get exception code
2043 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2052 CapturedBaseAddress
= *BaseAddress
;
2053 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2057 // Catch illegal base address
2059 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2062 // Catch illegal region size
2064 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2069 return STATUS_INVALID_PARAMETER
;
2073 // Get a reference to the process
2075 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2076 PROCESS_VM_OPERATION
,
2081 if (!NT_SUCCESS(Status
)) return Status
;
2086 Status
= MmFlushVirtualMemory(Process
,
2087 &CapturedBaseAddress
,
2088 &CapturedBytesToFlush
,
2092 // Release reference
2094 ObDereferenceObject(Process
);
2097 // Enter SEH to return data
2102 // Return data to user
2104 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2105 *NumberOfBytesToFlush
= 0;
2106 *IoStatusBlock
= LocalStatusBlock
;
2108 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2124 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2126 IN PVOID BaseAddress
,
2127 IN SIZE_T RegionSize
,
2128 IN PVOID
*UserAddressArray
,
2129 OUT PULONG_PTR EntriesInUserAddressArray
,
2130 OUT PULONG Granularity
)
2135 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2136 ULONG_PTR CapturedEntryCount
;
2140 // Check if we came from user mode
2142 if (PreviousMode
!= KernelMode
)
2145 // Enter SEH for probing
2150 // Catch illegal base address
2152 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2155 // Catch illegal region size
2157 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2162 return STATUS_INVALID_PARAMETER_3
;
2166 // Validate all data
2168 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2169 ProbeForWriteUlong(Granularity
);
2174 CapturedEntryCount
= *EntriesInUserAddressArray
;
2177 // Must have a count
2179 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2182 // Can't be larger than the maximum
2184 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2189 return STATUS_INVALID_PARAMETER_5
;
2193 // Probe the actual array
2195 ProbeForWrite(UserAddressArray
,
2196 CapturedEntryCount
* sizeof(PVOID
),
2199 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2202 // Get exception code
2204 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2213 CapturedEntryCount
= *EntriesInUserAddressArray
;
2214 ASSERT(CapturedEntryCount
!= 0);
2218 // Check if this is a local request
2220 if (ProcessHandle
== NtCurrentProcess())
2223 // No need to reference the process
2225 Process
= PsGetCurrentProcess();
2230 // Reference the target
2232 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2233 PROCESS_VM_OPERATION
,
2238 if (!NT_SUCCESS(Status
)) return Status
;
2242 // Compute the last address and validate it
2244 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2245 if (BaseAddress
> EndAddress
)
2250 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2251 return STATUS_INVALID_PARAMETER_4
;
2260 // Dereference if needed
2262 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2265 // Enter SEH to return data
2270 // Return data to user
2272 *EntriesInUserAddressArray
= 0;
2273 *Granularity
= PAGE_SIZE
;
2275 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2278 // Get exception code
2280 Status
= _SEH2_GetExceptionCode();
2287 return STATUS_SUCCESS
;
2295 NtResetWriteWatch(IN HANDLE ProcessHandle
,
2296 IN PVOID BaseAddress
,
2297 IN SIZE_T RegionSize
)
2302 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2303 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
2306 // Catch illegal base address
2308 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2311 // Catch illegal region size
2313 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2318 return STATUS_INVALID_PARAMETER_3
;
2322 // Check if this is a local request
2324 if (ProcessHandle
== NtCurrentProcess())
2327 // No need to reference the process
2329 Process
= PsGetCurrentProcess();
2334 // Reference the target
2336 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2337 PROCESS_VM_OPERATION
,
2342 if (!NT_SUCCESS(Status
)) return Status
;
2346 // Compute the last address and validate it
2348 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
2349 if (BaseAddress
> EndAddress
)
2354 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2355 return STATUS_INVALID_PARAMETER_3
;
2364 // Dereference if needed
2366 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
2371 return STATUS_SUCCESS
;
2376 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
2377 IN PVOID BaseAddress
,
2378 OUT PVOID MemoryInformation
,
2379 IN SIZE_T MemoryInformationLength
,
2380 OUT PSIZE_T ReturnLength
)
2382 PEPROCESS TargetProcess
;
2383 NTSTATUS Status
= STATUS_SUCCESS
;
2385 PVOID Address
, NextAddress
;
2386 BOOLEAN Found
= FALSE
;
2387 ULONG NewProtect
, NewState
;
2389 MEMORY_BASIC_INFORMATION MemoryInfo
;
2390 KAPC_STATE ApcState
;
2391 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2392 PMEMORY_AREA MemoryArea
;
2393 SIZE_T ResultLength
;
2395 /* Check for illegal addresses in user-space, or the shared memory area */
2396 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
2397 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
2399 Address
= PAGE_ALIGN(BaseAddress
);
2401 /* Make up an info structure describing this range */
2402 MemoryInfo
.BaseAddress
= Address
;
2403 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
2404 MemoryInfo
.Type
= MEM_PRIVATE
;
2406 /* Special case for shared data */
2407 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
2409 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
2410 MemoryInfo
.State
= MEM_COMMIT
;
2411 MemoryInfo
.Protect
= PAGE_READONLY
;
2412 MemoryInfo
.RegionSize
= PAGE_SIZE
;
2416 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
2417 MemoryInfo
.State
= MEM_RESERVE
;
2418 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2419 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
2422 /* Return the data, NtQueryInformation already probed it*/
2423 if (PreviousMode
!= KernelMode
)
2427 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2428 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2432 Status
= _SEH2_GetExceptionCode();
2438 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2439 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2445 /* Check if this is for a local or remote process */
2446 if (ProcessHandle
== NtCurrentProcess())
2448 TargetProcess
= PsGetCurrentProcess();
2452 /* Reference the target process */
2453 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2454 PROCESS_QUERY_INFORMATION
,
2456 ExGetPreviousMode(),
2457 (PVOID
*)&TargetProcess
,
2459 if (!NT_SUCCESS(Status
)) return Status
;
2461 /* Attach to it now */
2462 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
2466 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
2467 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
2469 /* Scan on the right */
2470 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
2471 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2474 /* Check if this VAD covers the allocation range */
2475 if ((BaseVpn
>= Vad
->StartingVpn
) &&
2476 (BaseVpn
<= Vad
->EndingVpn
))
2483 /* Check if this VAD is too high */
2484 if (BaseVpn
< Vad
->StartingVpn
)
2486 /* Stop if there is no left child */
2487 if (!Vad
->LeftChild
) break;
2489 /* Search on the left next */
2490 Vad
= Vad
->LeftChild
;
2494 /* Then this VAD is too low, keep searching on the right */
2495 ASSERT(BaseVpn
> Vad
->EndingVpn
);
2497 /* Stop if there is no right child */
2498 if (!Vad
->RightChild
) break;
2500 /* Search on the right next */
2501 Vad
= Vad
->RightChild
;
2506 /* Was a VAD found? */
2509 Address
= PAGE_ALIGN(BaseAddress
);
2511 /* Calculate region size */
2514 if (Vad
->StartingVpn
>= BaseVpn
)
2516 /* Region size is the free space till the start of that VAD */
2517 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2521 /* Get the next VAD */
2522 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
2525 /* Region size is the free space till the start of that VAD */
2526 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
2530 /* Maximum possible region size with that base address */
2531 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2537 /* Maximum possible region size with that base address */
2538 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
2541 /* Check if we were attached */
2542 if (ProcessHandle
!= NtCurrentProcess())
2544 /* Detach and derefernece the process */
2545 KeUnstackDetachProcess(&ApcState
);
2546 ObDereferenceObject(TargetProcess
);
2549 /* Build the rest of the initial information block */
2550 MemoryInfo
.BaseAddress
= Address
;
2551 MemoryInfo
.AllocationBase
= NULL
;
2552 MemoryInfo
.AllocationProtect
= 0;
2553 MemoryInfo
.State
= MEM_FREE
;
2554 MemoryInfo
.Protect
= PAGE_NOACCESS
;
2555 MemoryInfo
.Type
= 0;
2557 /* Return the data, NtQueryInformation already probed it*/
2558 if (PreviousMode
!= KernelMode
)
2562 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2563 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2565 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2567 Status
= _SEH2_GetExceptionCode();
2573 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2574 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2580 /* This must be a VM VAD */
2581 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
);
2583 /* Lock the address space of the process */
2584 MmLockAddressSpace(&TargetProcess
->Vm
);
2586 /* Find the memory area the specified address belongs to */
2587 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
2588 ASSERT(MemoryArea
!= NULL
);
2590 /* Determine information dependent on the memory area type */
2591 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
2593 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
2594 ASSERT(NT_SUCCESS(Status
));
2598 /* Build the initial information block */
2599 Address
= PAGE_ALIGN(BaseAddress
);
2600 MemoryInfo
.BaseAddress
= Address
;
2601 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
2602 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2603 MemoryInfo
.Type
= MEM_PRIVATE
;
2605 /* Find the largest chunk of memory which has the same state and protection mask */
2606 MemoryInfo
.State
= MiQueryAddressState(Address
,
2609 &MemoryInfo
.Protect
,
2611 Address
= NextAddress
;
2612 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
2614 /* Keep going unless the state or protection mask changed */
2615 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
2616 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
2617 Address
= NextAddress
;
2620 /* Now that we know the last VA address, calculate the region size */
2621 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
2624 /* Unlock the address space of the process */
2625 MmUnlockAddressSpace(&TargetProcess
->Vm
);
2627 /* Check if we were attached */
2628 if (ProcessHandle
!= NtCurrentProcess())
2630 /* Detach and derefernece the process */
2631 KeUnstackDetachProcess(&ApcState
);
2632 ObDereferenceObject(TargetProcess
);
2635 /* Return the data, NtQueryInformation already probed it*/
2636 if (PreviousMode
!= KernelMode
)
2640 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2641 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2643 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2645 Status
= _SEH2_GetExceptionCode();
2651 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
2652 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
2656 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2657 "State: %lx Type: %lx Size: %lx\n",
2658 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
2659 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
2660 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
2667 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
2668 IN PVOID BaseAddress
,
2669 OUT PVOID MemoryInformation
,
2670 IN SIZE_T MemoryInformationLength
,
2671 OUT PSIZE_T ReturnLength
)
2675 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
2676 UNICODE_STRING ModuleFileName
;
2677 PMEMORY_SECTION_NAME SectionName
= NULL
;
2678 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2680 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2681 PROCESS_QUERY_INFORMATION
,
2687 if (!NT_SUCCESS(Status
))
2689 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
2693 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
2694 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
2696 if (NT_SUCCESS(Status
))
2698 SectionName
= MemoryInformation
;
2699 if (PreviousMode
!= KernelMode
)
2703 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2704 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2705 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2707 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2710 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2712 Status
= _SEH2_GetExceptionCode();
2718 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
2719 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
2720 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
2722 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
2726 ObDereferenceObject(Process
);
2732 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
2733 IN PVOID BaseAddress
,
2734 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
2735 OUT PVOID MemoryInformation
,
2736 IN SIZE_T MemoryInformationLength
,
2737 OUT PSIZE_T ReturnLength
)
2739 NTSTATUS Status
= STATUS_SUCCESS
;
2740 KPROCESSOR_MODE PreviousMode
;
2742 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
2744 /* Bail out if the address is invalid */
2745 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2747 /* Probe return buffer */
2748 PreviousMode
= ExGetPreviousMode();
2749 if (PreviousMode
!= KernelMode
)
2753 ProbeForWrite(MemoryInformation
,
2754 MemoryInformationLength
,
2757 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
2759 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2761 Status
= _SEH2_GetExceptionCode();
2765 if (!NT_SUCCESS(Status
))
2771 switch(MemoryInformationClass
)
2773 case MemoryBasicInformation
:
2774 /* Validate the size information of the class */
2775 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
2777 /* The size is invalid */
2778 return STATUS_INFO_LENGTH_MISMATCH
;
2780 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
2783 MemoryInformationLength
,
2787 case MemorySectionName
:
2788 /* Validate the size information of the class */
2789 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
2791 /* The size is invalid */
2792 return STATUS_INFO_LENGTH_MISMATCH
;
2794 Status
= MiQueryMemorySectionName(ProcessHandle
,
2797 MemoryInformationLength
,
2800 case MemoryWorkingSetList
:
2801 case MemoryBasicVlmInformation
:
2803 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
2816 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
2817 IN OUT PVOID
* UBaseAddress
,
2818 IN ULONG_PTR ZeroBits
,
2819 IN OUT PSIZE_T URegionSize
,
2820 IN ULONG AllocationType
,
2825 NTSTATUS Status
= STATUS_SUCCESS
;
2829 PMMADDRESS_NODE ParentNode
;
2830 ULONG_PTR StartVpn
, EndVpn
;
2831 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
2832 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2833 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
2834 KAPC_STATE ApcState
;
2835 ULONG ProtectionMask
;
2836 BOOLEAN Attached
= FALSE
;
2837 BoundaryAddressMultiple
.QuadPart
= 0;
2838 TABLE_SEARCH_RESULT Result
;
2842 /* Check for valid Zero bits */
2845 DPRINT1("Too many zero bits\n");
2846 return STATUS_INVALID_PARAMETER_3
;
2849 /* Check for valid Allocation Types */
2850 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
2851 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
2853 DPRINT1("Invalid Allocation Type\n");
2854 return STATUS_INVALID_PARAMETER_5
;
2857 /* Check for at least one of these Allocation Types to be set */
2858 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
2860 DPRINT1("No memory allocation base type\n");
2861 return STATUS_INVALID_PARAMETER_5
;
2864 /* MEM_RESET is an exclusive flag, make sure that is valid too */
2865 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
2867 DPRINT1("Invalid use of MEM_RESET\n");
2868 return STATUS_INVALID_PARAMETER_5
;
2871 /* Check if large pages are being used */
2872 if (AllocationType
& MEM_LARGE_PAGES
)
2874 /* Large page allocations MUST be committed */
2875 if (!(AllocationType
& MEM_COMMIT
))
2877 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
2878 return STATUS_INVALID_PARAMETER_5
;
2881 /* These flags are not allowed with large page allocations */
2882 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
2884 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
2885 return STATUS_INVALID_PARAMETER_5
;
2889 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
2890 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
2892 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2893 return STATUS_INVALID_PARAMETER_5
;
2896 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
2897 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
2899 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2900 return STATUS_INVALID_PARAMETER_5
;
2903 /* Check for valid MEM_PHYSICAL usage */
2904 if (AllocationType
& MEM_PHYSICAL
)
2906 /* Only these flags are allowed with MEM_PHYSIAL */
2907 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
2909 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
2910 return STATUS_INVALID_PARAMETER_5
;
2913 /* Then make sure PAGE_READWRITE is used */
2914 if (Protect
!= PAGE_READWRITE
)
2916 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
2917 return STATUS_INVALID_PARAMETER_6
;
2921 /* Calculate the protection mask and make sure it's valid */
2922 ProtectionMask
= MiMakeProtectionMask(Protect
);
2923 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2925 DPRINT1("Invalid protection mask\n");
2926 return STATUS_INVALID_PAGE_PROTECTION
;
2932 /* Check for user-mode parameters */
2933 if (PreviousMode
!= KernelMode
)
2935 /* Make sure they are writable */
2936 ProbeForWritePointer(UBaseAddress
);
2937 ProbeForWriteUlong(URegionSize
);
2940 /* Capture their values */
2941 BaseAddress
= *UBaseAddress
;
2942 RegionSize
= *URegionSize
;
2944 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2946 /* Return the exception code */
2947 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2951 /* Make sure there's a size specified */
2954 DPRINT1("Region size is invalid (zero)\n");
2955 return STATUS_INVALID_PARAMETER_4
;
2958 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)BaseAddress
+ RegionSize
) -
2959 PAGE_ROUND_DOWN(BaseAddress
);
2960 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(BaseAddress
);
2961 StartVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
2962 EndVpn
= ((ULONG_PTR
)BaseAddress
+ RegionSize
- 1) >> PAGE_SHIFT
;
2964 /* Make sure the allocation isn't past the VAD area */
2965 if (BaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
2967 DPRINT1("Virtual allocation base above User Space\n");
2968 return STATUS_INVALID_PARAMETER_2
;
2971 /* Make sure the allocation wouldn't overflow past the VAD area */
2972 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)BaseAddress
) < RegionSize
)
2974 DPRINT1("Region size would overflow into kernel-memory\n");
2975 return STATUS_INVALID_PARAMETER_4
;
2978 /* Check if this is for the current process */
2979 if (ProcessHandle
== NtCurrentProcess())
2981 /* We already have the current process, no need to go through Ob */
2982 Process
= CurrentProcess
;
2986 /* Reference the handle for correct permissions */
2987 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2988 PROCESS_VM_OPERATION
,
2993 if (!NT_SUCCESS(Status
)) return Status
;
2995 /* Check if not running in the current process */
2996 if (CurrentProcess
!= Process
)
2999 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3004 /* Check for large page allocations */
3005 if (AllocationType
& MEM_LARGE_PAGES
)
3007 /* The lock memory privilege is required */
3008 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3010 /* Fail without it */
3011 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3012 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3013 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3014 return STATUS_PRIVILEGE_NOT_HELD
;
3020 * Copy on Write is reserved for system use. This case is a certain failure
3021 * but there may be other cases...needs more testing
3023 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
3024 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
3026 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
3027 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3028 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3029 return STATUS_INVALID_PAGE_PROTECTION
;
3032 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
3033 DPRINT("Type %x\n", Type
);
3035 /* Lock the process address space */
3036 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
3038 if(BaseAddress
!= 0)
3041 * An address was provided. Let's see if we've already
3044 if(MiCheckForConflictingNode(StartVpn
, EndVpn
, &Process
->VadRoot
) != NULL
)
3046 /* Can't reserve twice the same range */
3047 if(AllocationType
& MEM_RESERVE
)
3049 Status
= STATUS_CONFLICTING_ADDRESSES
;
3050 DPRINT1("Trying to reserve twice the same range.\n");
3053 /* Great there's already something there. What shall we do ? */
3054 if(AllocationType
== MEM_RESET
)
3057 /* Reset the dirty bits for each PTEs */
3062 ASSERT(AllocationType
& MEM_COMMIT
);
3064 /* Mark the VAD as committed */
3069 /* There's nothing */
3070 if(!(AllocationType
& MEM_RESERVE
))
3072 Status
= STATUS_ACCESS_DENIED
;
3076 /* Now we can reserve our chunk of memory */
3080 /* No base address was given. */
3081 if(!(AllocationType
& MEM_RESERVE
))
3083 DPRINT1("Providing NULL base address witout MEM_RESERVE.\n");
3085 Status
= STATUS_INVALID_PARAMETER_5
;
3089 /* Find an empty range in Address Space */
3090 if(AllocationType
& MEM_TOP_DOWN
)
3092 /* Top down allocation */
3093 Result
= MiFindEmptyAddressRangeDownTree(RegionSize
,
3094 (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
,
3095 (ZeroBits
> PAGE_SHIFT
) ? 1 << ZeroBits
: PAGE_SIZE
,
3097 (PULONG_PTR
)&BaseAddress
,
3100 if(Result
== TableFoundNode
)
3102 /* This means failure */
3103 Status
= STATUS_NO_MEMORY
;
3109 /* Good old bottom up allocation */
3110 Status
= MiFindEmptyAddressRangeInTree(RegionSize
,
3111 (ZeroBits
> PAGE_SHIFT
) ? 1 << ZeroBits
: PAGE_SIZE
,
3114 (PULONG_PTR
)&BaseAddress
);
3115 if(!NT_SUCCESS(Status
))
3121 StartVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
3122 EndVpn
= ((ULONG_PTR
)BaseAddress
+ RegionSize
- 1) >> PAGE_SHIFT
;
3126 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD
), TAG_MVAD
);
3129 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3132 RtlZeroMemory(Vad
, sizeof(MMVAD
));
3135 Vad
->StartingVpn
= StartVpn
;
3136 Vad
->EndingVpn
= EndVpn
;
3137 /* Set protection */
3138 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3139 /* Should it be already marked as committed ? */
3140 if(AllocationType
& MEM_COMMIT
)
3141 Vad
->u
.VadFlags
.MemCommit
= 1;
3142 if(AllocationType
& MEM_PHYSICAL
)
3145 Vad
->u
.VadFlags
.VadType
= VadAwe
;
3148 MiLockProcessWorkingSet(Process
, PsGetCurrentThread());
3149 MiInsertVad(Vad
, Process
);
3150 MiUnlockProcessWorkingSet(Process
, PsGetCurrentThread());
3154 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
3155 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3156 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3158 *UBaseAddress
= BaseAddress
;
3159 *URegionSize
= RegionSize
;
3160 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);