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 *******************************************************************/
10 /* So long, and Thanks for All the Fish */
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
32 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
34 IN ULONG ProtectionMask
,
36 IN BOOLEAN CaptureDirtyBit
);
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
44 IN ULONG_PTR EndingAddress
,
48 PMMPTE PointerPte
, LastPte
, PointerPde
;
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde
= MiAddressToPde(StartingAddress
);
53 PointerPte
= MiAddressToPte(StartingAddress
);
54 LastPte
= MiAddressToPte(EndingAddress
);
56 /* Handle commited pages first */
57 if (Vad
->u
.VadFlags
.MemCommit
== 1)
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages
= (ULONG
)BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
62 /* Is the PDE demand-zero? */
63 PointerPde
= MiAddressToPte(PointerPte
);
64 if (PointerPde
->u
.Long
!= 0)
66 /* It is not. Is it valid? */
67 if (PointerPde
->u
.Hard
.Valid
== 0)
70 PointerPte
= MiPteToAddress(PointerPde
);
71 MiMakeSystemAddressValid(PointerPte
, Process
);
76 /* It is, skip it and move to the next PDE, unless we're done */
78 PointerPte
= MiPteToAddress(PointerPde
);
79 if (PointerPte
> LastPte
) return CommittedPages
;
82 /* Now loop all the PTEs in the range */
83 while (PointerPte
<= LastPte
)
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte
))
88 /* Is this PDE demand zero? */
89 PointerPde
= MiAddressToPte(PointerPte
);
90 if (PointerPde
->u
.Long
!= 0)
92 /* It isn't -- is it valid? */
93 if (PointerPde
->u
.Hard
.Valid
== 0)
95 /* Nope, fault it in */
96 PointerPte
= MiPteToAddress(PointerPde
);
97 MiMakeSystemAddressValid(PointerPte
, Process
);
102 /* It is, skip it and move to the next PDE */
104 PointerPte
= MiPteToAddress(PointerPde
);
109 /* Is this PTE demand zero? */
110 if (PointerPte
->u
.Long
!= 0)
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
114 (PointerPte
->u
.Hard
.Valid
== 0) &&
115 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
116 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
118 /* It is, so remove it from the count of commited pages */
123 /* Move to the next PTE */
127 /* Return how many committed pages there still are */
128 return CommittedPages
;
131 /* This is a non-commited VAD, so assume none of it is committed */
134 /* Is the PDE demand-zero? */
135 PointerPde
= MiAddressToPte(PointerPte
);
136 if (PointerPde
->u
.Long
!= 0)
138 /* It isn't -- is it invalid? */
139 if (PointerPde
->u
.Hard
.Valid
== 0)
141 /* It is, so page it in */
142 PointerPte
= MiPteToAddress(PointerPde
);
143 MiMakeSystemAddressValid(PointerPte
, Process
);
148 /* It is, so skip it and move to the next PDE */
150 PointerPte
= MiPteToAddress(PointerPde
);
151 if (PointerPte
> LastPte
) return CommittedPages
;
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte
<= LastPte
)
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte
))
160 /* Is this new PDE demand-zero? */
161 PointerPde
= MiAddressToPte(PointerPte
);
162 if (PointerPde
->u
.Long
!= 0)
164 /* It isn't. Is it valid? */
165 if (PointerPde
->u
.Hard
.Valid
== 0)
167 /* It isn't, so make it valid */
168 PointerPte
= MiPteToAddress(PointerPde
);
169 MiMakeSystemAddressValid(PointerPte
, Process
);
174 /* It is, so skip it and move to the next one */
176 PointerPte
= MiPteToAddress(PointerPde
);
181 /* Is this PTE demand-zero? */
182 if (PointerPte
->u
.Long
!= 0)
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
186 (PointerPte
->u
.Hard
.Valid
== 1) ||
187 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
188 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
190 /* It is! So we'll treat this as a committed page */
195 /* Move to the next PTE */
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages
;
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
206 IN PEPROCESS CurrentProcess
)
209 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
210 PETHREAD CurrentThread
= PsGetCurrentThread();
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
214 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
215 (PageTableVirtualAddress
> MmPagedPoolEnd
));
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE
);
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress
))
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
230 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
231 if (!NT_SUCCESS(Status
))
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
237 (ULONG_PTR
)CurrentProcess
,
238 (ULONG_PTR
)PageTableVirtualAddress
);
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess
,
247 /* This flag will be useful later when we do better locking */
251 /* Let caller know what the lock state is */
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
261 BOOLEAN LockChange
= FALSE
;
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress
))
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
273 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
274 if (!NT_SUCCESS(Status
))
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
281 (ULONG_PTR
)VirtualAddress
);
284 /* This flag will be useful later when we do better locking */
287 /* Lock the PFN database */
288 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
291 /* Let caller know what the lock state is */
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
298 IN PFN_NUMBER PageCount
,
300 OUT PPFN_NUMBER ValidPages
)
302 PFN_COUNT ActualPages
= 0;
303 PETHREAD CurrentThread
= PsGetCurrentThread();
305 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
315 /* Make sure there's some data about the page */
316 if (PointerPte
->u
.Long
)
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
320 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte
->u
.Hard
.Valid
)
325 /* Get the page PFN */
326 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
327 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1
->u1
.WsIndex
== 0);
332 /* Actual valid, legitimate, pages */
333 if (ValidPages
) (*ValidPages
)++;
335 /* Get the page table entry */
336 PageTableIndex
= Pfn1
->u4
.PteFrame
;
337 Pfn2
= MiGetPfnEntry(PageTableIndex
);
339 /* Lock the PFN database */
340 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1
);
344 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2
, PageTableIndex
);
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte
);
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
362 * Right now, we shouldn't expect any page file information in the PTE
364 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte
);
370 /* Actual legitimate pages */
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE
, TRUE
);
391 MiDeletePte(IN PMMPTE PointerPte
,
392 IN PVOID VirtualAddress
,
393 IN PEPROCESS CurrentProcess
,
394 IN PMMPTE PrototypePte
)
398 PFN_NUMBER PageFrameIndex
;
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
404 /* Capture the PTE */
405 TempPte
= *PointerPte
;
407 /* See if the PTE is valid */
408 if (TempPte
.u
.Hard
.Valid
== 0)
410 /* Prototype and paged out PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 ASSERT((TempPte
.u
.Soft
.PageFileHigh
== 0) || (TempPte
.u
.Soft
.Transition
== 1));
414 if (TempPte
.u
.Soft
.Transition
)
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 DPRINT("Pte %p is transitional!\n", PointerPte
);
422 /* Destroy the PTE */
423 MI_ERASE_PTE(PointerPte
);
425 /* Drop the reference on the page table. */
426 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
428 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
430 /* Make the page free. For prototypes, it will be made free when deleting the section object */
431 if (Pfn1
->u2
.ShareCount
== 0)
433 NT_ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
435 /* And it should be in standby or modified list */
436 ASSERT((Pfn1
->u3
.e1
.PageLocation
== ModifiedPageList
) || (Pfn1
->u3
.e1
.PageLocation
== StandbyPageList
));
438 /* Unlink it and temporarily mark it as active */
439 MiUnlinkPageFromList(Pfn1
);
440 Pfn1
->u3
.e2
.ReferenceCount
++;
441 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
443 /* This will put it back in free list and clean properly up */
444 MI_SET_PFN_DELETED(Pfn1
);
445 MiDecrementReferenceCount(Pfn1
, PageFrameIndex
);
451 /* Get the PFN entry */
452 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
453 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
455 /* Check if this is a valid, prototype PTE */
456 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
458 /* Get the PDE and make sure it's faulted in */
459 PointerPde
= MiPteToPde(PointerPte
);
460 if (PointerPde
->u
.Hard
.Valid
== 0)
462 #if (_MI_PAGING_LEVELS == 2)
463 /* Could be paged pool access from a new process -- synchronize the page directories */
464 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
467 /* The PDE must be valid at this point */
468 KeBugCheckEx(MEMORY_MANAGEMENT
,
470 (ULONG_PTR
)PointerPte
,
472 (ULONG_PTR
)VirtualAddress
);
474 #if (_MI_PAGING_LEVELS == 2)
477 /* Drop the share count on the page table */
478 PointerPde
= MiPteToPde(PointerPte
);
479 MiDecrementShareCount(MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
),
480 PointerPde
->u
.Hard
.PageFrameNumber
);
482 /* Drop the share count */
483 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
485 /* Either a fork, or this is the shared user data page */
486 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
488 /* If it's not the shared user page, then crash, since there's no fork() yet */
489 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
490 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
492 /* Must be some sort of memory corruption */
493 KeBugCheckEx(MEMORY_MANAGEMENT
,
495 (ULONG_PTR
)PointerPte
,
496 (ULONG_PTR
)PrototypePte
,
497 (ULONG_PTR
)Pfn1
->PteAddress
);
502 MI_ERASE_PTE(PointerPte
);
506 /* Make sure the saved PTE address is valid */
507 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
509 /* The PFN entry is illegal, or invalid */
510 KeBugCheckEx(MEMORY_MANAGEMENT
,
512 (ULONG_PTR
)PointerPte
,
514 (ULONG_PTR
)Pfn1
->PteAddress
);
518 MI_ERASE_PTE(PointerPte
);
520 /* There should only be 1 shared reference count */
521 ASSERT(Pfn1
->u2
.ShareCount
== 1);
523 /* Drop the reference on the page table. */
524 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
526 /* Mark the PFN for deletion and dereference what should be the last ref */
527 MI_SET_PFN_DELETED(Pfn1
);
528 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
530 /* We should eventually do this */
531 //CurrentProcess->NumberOfPrivatePages--;
540 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
541 IN ULONG_PTR EndingAddress
,
544 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
547 PEPROCESS CurrentProcess
;
549 BOOLEAN AddressGap
= FALSE
;
550 PSUBSECTION Subsection
;
552 /* Get out if this is a fake VAD, RosMm will free the marea pages */
553 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
555 /* Grab the process and PTE/PDE for the address being deleted */
556 CurrentProcess
= PsGetCurrentProcess();
557 PointerPde
= MiAddressToPde(Va
);
558 PointerPte
= MiAddressToPte(Va
);
560 /* Check if this is a section VAD or a VM VAD */
561 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
563 /* Don't worry about prototypes */
564 PrototypePte
= LastPrototypePte
= NULL
;
568 /* Get the prototype PTE */
569 PrototypePte
= Vad
->FirstPrototypePte
;
570 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
573 /* In all cases, we don't support fork() yet */
574 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
576 /* Loop the PTE for each VA */
579 /* First keep going until we find a valid PDE */
580 while (!PointerPde
->u
.Long
)
582 /* There are gaps in the address space */
585 /* Still no valid PDE, try the next 4MB (or whatever) */
588 /* Update the PTE on this new boundary */
589 PointerPte
= MiPteToAddress(PointerPde
);
591 /* Check if all the PDEs are invalid, so there's nothing to free */
592 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
593 if (Va
> EndingAddress
) return;
596 /* Now check if the PDE is mapped in */
597 if (!PointerPde
->u
.Hard
.Valid
)
599 /* It isn't, so map it in */
600 PointerPte
= MiPteToAddress(PointerPde
);
601 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
604 /* Now we should have a valid PDE, mapped in, and still have some VA */
605 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
606 ASSERT(Va
<= EndingAddress
);
608 /* Check if this is a section VAD with gaps in it */
609 if ((AddressGap
) && (LastPrototypePte
))
611 /* We need to skip to the next correct prototype PTE */
612 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
614 /* And we need the subsection to skip to the next last prototype PTE */
615 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
619 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
623 /* No more subsections, we are done with prototype PTEs */
628 /* Lock the PFN Database while we delete the PTEs */
629 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
632 /* Capture the PDE and make sure it exists */
633 TempPte
= *PointerPte
;
636 MiDecrementPageTableReferences((PVOID
)Va
);
638 /* Check if the PTE is actually mapped in */
639 if (MI_IS_MAPPED_PTE(&TempPte
))
641 /* Are we dealing with section VAD? */
642 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
644 /* We need to skip to the next correct prototype PTE */
645 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
647 /* And we need the subsection to skip to the next last prototype PTE */
648 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
652 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
656 /* No more subsections, we are done with prototype PTEs */
661 /* Check for prototype PTE */
662 if ((TempPte
.u
.Hard
.Valid
== 0) &&
663 (TempPte
.u
.Soft
.Prototype
== 1))
666 MI_ERASE_PTE(PointerPte
);
670 /* Delete the PTE proper */
671 MiDeletePte(PointerPte
,
679 /* The PTE was never mapped, just nuke it here */
680 MI_ERASE_PTE(PointerPte
);
684 /* Update the address and PTE for it */
689 /* Making sure the PDE is still valid */
690 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
692 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
694 /* The PDE should still be valid at this point */
695 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
697 /* Check remaining PTE count (go back 1 page due to above loop) */
698 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
700 if (PointerPde
->u
.Long
!= 0)
702 /* Delete the PTE proper */
703 MiDeletePte(PointerPde
,
704 MiPteToAddress(PointerPde
),
710 /* Release the lock and get out if we're done */
711 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
712 if (Va
> EndingAddress
) return;
714 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
715 PointerPde
= MiAddressToPde(Va
);
721 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
722 OUT PBOOLEAN HaveBadAddress
,
723 OUT PULONG_PTR BadAddress
)
725 PEXCEPTION_RECORD ExceptionRecord
;
731 *HaveBadAddress
= FALSE
;
734 // Get the exception record
736 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
739 // Look at the exception code
741 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
742 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
743 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
746 // We can tell the address if we have more than one parameter
748 if (ExceptionRecord
->NumberParameters
> 1)
751 // Return the address
753 *HaveBadAddress
= TRUE
;
754 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
759 // Continue executing the next handler
761 return EXCEPTION_EXECUTE_HANDLER
;
766 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
767 IN PVOID SourceAddress
,
768 IN PEPROCESS TargetProcess
,
769 OUT PVOID TargetAddress
,
770 IN SIZE_T BufferSize
,
771 IN KPROCESSOR_MODE PreviousMode
,
772 OUT PSIZE_T ReturnSize
)
774 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
775 PMDL Mdl
= (PMDL
)MdlBuffer
;
776 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
777 volatile BOOLEAN FailedInProbe
= FALSE
;
778 volatile BOOLEAN PagesLocked
= FALSE
;
779 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
780 volatile PVOID MdlAddress
= NULL
;
782 BOOLEAN HaveBadAddress
;
783 ULONG_PTR BadAddress
;
784 NTSTATUS Status
= STATUS_SUCCESS
;
788 // Calculate the maximum amount of data to move
790 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
791 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
792 CurrentSize
= TotalSize
;
793 RemainingSize
= BufferSize
;
796 // Loop as long as there is still data
798 while (RemainingSize
> 0)
801 // Check if this transfer will finish everything off
803 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
806 // Attach to the source address space
808 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
811 // Check state for this pass
813 ASSERT(MdlAddress
== NULL
);
814 ASSERT(PagesLocked
== FALSE
);
815 ASSERT(FailedInProbe
== FALSE
);
818 // Protect user-mode copy
823 // If this is our first time, probe the buffer
825 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
828 // Catch a failure here
830 FailedInProbe
= TRUE
;
835 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
840 FailedInProbe
= FALSE
;
844 // Initialize and probe and lock the MDL
846 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
847 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
850 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
852 Status
= _SEH2_GetExceptionCode();
856 /* Detach from source process */
857 KeUnstackDetachProcess(&ApcState
);
859 if (Status
!= STATUS_SUCCESS
)
867 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
875 Status
= STATUS_INSUFFICIENT_RESOURCES
;
880 // Grab to the target process
882 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
887 // Check if this is our first time through
889 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
892 // Catch a failure here
894 FailedInProbe
= TRUE
;
899 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
904 FailedInProbe
= FALSE
;
908 // Now do the actual move
910 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
912 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
916 *ReturnSize
= BufferSize
- RemainingSize
;
918 // Check if we failed during the probe
925 Status
= _SEH2_GetExceptionCode();
930 // Othewise we failed during the move.
931 // Check if we know exactly where we stopped copying
936 // Return the exact number of bytes copied
938 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
941 // Return partial copy
943 Status
= STATUS_PARTIAL_COPY
;
948 /* Detach from target process */
949 KeUnstackDetachProcess(&ApcState
);
952 // Check for SEH status
954 if (Status
!= STATUS_SUCCESS
)
962 MmUnmapLockedPages(MdlAddress
, Mdl
);
968 // Update location and size
970 RemainingSize
-= CurrentSize
;
971 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
972 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
976 if (MdlAddress
!= NULL
)
977 MmUnmapLockedPages(MdlAddress
, Mdl
);
984 if (Status
== STATUS_SUCCESS
)
985 *ReturnSize
= BufferSize
;
991 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
992 IN PVOID SourceAddress
,
993 IN PEPROCESS TargetProcess
,
994 OUT PVOID TargetAddress
,
995 IN SIZE_T BufferSize
,
996 IN KPROCESSOR_MODE PreviousMode
,
997 OUT PSIZE_T ReturnSize
)
999 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
1000 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
1001 volatile BOOLEAN FailedInProbe
= FALSE
, HavePoolAddress
= FALSE
;
1002 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
1004 KAPC_STATE ApcState
;
1005 BOOLEAN HaveBadAddress
;
1006 ULONG_PTR BadAddress
;
1007 NTSTATUS Status
= STATUS_SUCCESS
;
1010 DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1011 BufferSize
, SourceProcess
, SourceAddress
, TargetProcess
, TargetAddress
);
1014 // Calculate the maximum amount of data to move
1016 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1017 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1018 CurrentSize
= TotalSize
;
1019 RemainingSize
= BufferSize
;
1022 // Check if we can use the stack
1024 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1029 PoolAddress
= (PVOID
)StackBuffer
;
1036 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1037 if (!PoolAddress
) ASSERT(FALSE
);
1038 HavePoolAddress
= TRUE
;
1042 // Loop as long as there is still data
1044 while (RemainingSize
> 0)
1047 // Check if this transfer will finish everything off
1049 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1052 // Attach to the source address space
1054 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1056 /* Check that state is sane */
1057 ASSERT(FailedInProbe
== FALSE
);
1058 ASSERT(Status
== STATUS_SUCCESS
);
1061 // Protect user-mode copy
1066 // If this is our first time, probe the buffer
1068 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1071 // Catch a failure here
1073 FailedInProbe
= TRUE
;
1078 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1083 FailedInProbe
= FALSE
;
1089 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1091 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1095 *ReturnSize
= BufferSize
- RemainingSize
;
1098 // Check if we failed during the probe
1105 Status
= _SEH2_GetExceptionCode();
1110 // We failed during the move.
1111 // Check if we know exactly where we stopped copying
1116 // Return the exact number of bytes copied
1118 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1121 // Return partial copy
1123 Status
= STATUS_PARTIAL_COPY
;
1128 /* Let go of the source */
1129 KeUnstackDetachProcess(&ApcState
);
1131 if (Status
!= STATUS_SUCCESS
)
1136 /* Grab the target process */
1137 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1142 // Check if this is our first time through
1144 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
1147 // Catch a failure here
1149 FailedInProbe
= TRUE
;
1154 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1159 FailedInProbe
= FALSE
;
1163 // Now do the actual move
1165 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1167 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1171 *ReturnSize
= BufferSize
- RemainingSize
;
1173 // Check if we failed during the probe
1180 Status
= _SEH2_GetExceptionCode();
1185 // Otherwise we failed during the move.
1186 // Check if we know exactly where we stopped copying
1191 // Return the exact number of bytes copied
1193 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1196 // Return partial copy
1198 Status
= STATUS_PARTIAL_COPY
;
1204 // Detach from target
1206 KeUnstackDetachProcess(&ApcState
);
1209 // Check for SEH status
1211 if (Status
!= STATUS_SUCCESS
)
1217 // Update location and size
1219 RemainingSize
-= CurrentSize
;
1220 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1221 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1227 // Check if we had allocated pool
1229 if (HavePoolAddress
)
1230 ExFreePoolWithTag(PoolAddress
, 'VmRw');
1235 if (Status
== STATUS_SUCCESS
)
1236 *ReturnSize
= BufferSize
;
1242 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1243 IN PVOID SourceAddress
,
1244 IN PEPROCESS TargetProcess
,
1245 OUT PVOID TargetAddress
,
1246 IN SIZE_T BufferSize
,
1247 IN KPROCESSOR_MODE PreviousMode
,
1248 OUT PSIZE_T ReturnSize
)
1251 PEPROCESS Process
= SourceProcess
;
1254 // Don't accept zero-sized buffers
1256 if (!BufferSize
) return STATUS_SUCCESS
;
1259 // If we are copying from ourselves, lock the target instead
1261 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1264 // Acquire rundown protection
1266 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1271 return STATUS_PROCESS_IS_TERMINATING
;
1275 // See if we should use the pool copy
1277 if (BufferSize
> MI_POOL_COPY_BYTES
)
1282 Status
= MiDoMappedCopy(SourceProcess
,
1295 Status
= MiDoPoolCopy(SourceProcess
,
1307 ExReleaseRundownProtection(&Process
->RundownProtect
);
1313 MmFlushVirtualMemory(IN PEPROCESS Process
,
1314 IN OUT PVOID
*BaseAddress
,
1315 IN OUT PSIZE_T RegionSize
,
1316 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1324 return STATUS_SUCCESS
;
1329 MiGetPageProtection(IN PMMPTE PointerPte
)
1333 PEPROCESS CurrentProcess
;
1334 PETHREAD CurrentThread
;
1335 BOOLEAN WsSafe
, WsShared
;
1340 /* Copy this PTE's contents */
1341 TempPte
= *PointerPte
;
1343 /* Assure it's not totally zero */
1344 ASSERT(TempPte
.u
.Long
);
1346 /* Check for a special prototype format */
1347 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1348 (TempPte
.u
.Soft
.Prototype
== 1))
1350 /* Check if the prototype PTE is not yet pointing to a PTE */
1351 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1353 /* The prototype PTE contains the protection */
1354 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1357 /* Get a pointer to the underlying shared PTE */
1358 PointerPte
= MiProtoPteToPte(&TempPte
);
1360 /* Since the PTE we want to read can be paged out at any time, we need
1361 to release the working set lock first, so that it can be paged in */
1362 CurrentThread
= PsGetCurrentThread();
1363 CurrentProcess
= PsGetCurrentProcess();
1364 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1369 /* Now read the PTE value */
1370 TempPte
= *PointerPte
;
1372 /* Check if that one is invalid */
1373 if (!TempPte
.u
.Hard
.Valid
)
1375 /* We get the protection directly from this PTE */
1376 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1380 /* The PTE is valid, so we might need to get the protection from
1381 the PFN. Lock the PFN database */
1382 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1384 /* Check if the PDE is still valid */
1385 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1387 /* It's not, make it valid */
1388 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1391 /* Now it's safe to read the PTE value again */
1392 TempPte
= *PointerPte
;
1393 ASSERT(TempPte
.u
.Long
!= 0);
1395 /* Check again if the PTE is invalid */
1396 if (!TempPte
.u
.Hard
.Valid
)
1398 /* The PTE is not valid, so we can use it's protection field */
1399 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1403 /* The PTE is valid, so we can find the protection in the
1404 OriginalPte field of the PFN */
1405 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1406 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1409 /* Release the PFN database */
1410 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1413 /* Lock the working set again */
1414 MiLockProcessWorkingSetForFault(CurrentProcess
,
1422 /* In the easy case of transition or demand zero PTE just return its protection */
1423 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1425 /* If we get here, the PTE is valid, so look up the page in PFN database */
1426 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1427 if (!Pfn
->u3
.e1
.PrototypePte
)
1429 /* Return protection of the original pte */
1430 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1431 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1434 /* This is software PTE */
1435 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1436 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1437 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1438 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1439 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1444 MiQueryAddressState(IN PVOID Va
,
1446 IN PEPROCESS TargetProcess
,
1447 OUT PULONG ReturnedProtect
,
1451 PMMPTE PointerPte
, ProtoPte
;
1453 #if (_MI_PAGING_LEVELS >= 3)
1456 #if (_MI_PAGING_LEVELS >= 4)
1459 MMPTE TempPte
, TempProtoPte
;
1460 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1461 ULONG State
= MEM_RESERVE
, Protect
= 0;
1462 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1463 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1465 /* Only normal VADs supported */
1466 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1468 /* Get the PDE and PTE for the address */
1469 PointerPde
= MiAddressToPde(Va
);
1470 PointerPte
= MiAddressToPte(Va
);
1471 #if (_MI_PAGING_LEVELS >= 3)
1472 PointerPpe
= MiAddressToPpe(Va
);
1474 #if (_MI_PAGING_LEVELS >= 4)
1475 PointerPxe
= MiAddressToPxe(Va
);
1478 /* Return the next range */
1479 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1483 #if (_MI_PAGING_LEVELS >= 4)
1484 /* Does the PXE exist? */
1485 if (PointerPxe
->u
.Long
== 0)
1487 /* It does not, next range starts at the next PXE */
1488 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1492 /* Is the PXE valid? */
1493 if (PointerPxe
->u
.Hard
.Valid
== 0)
1495 /* Is isn't, fault it in (make the PPE accessible) */
1496 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1499 #if (_MI_PAGING_LEVELS >= 3)
1500 /* Does the PPE exist? */
1501 if (PointerPpe
->u
.Long
== 0)
1503 /* It does not, next range starts at the next PPE */
1504 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1508 /* Is the PPE valid? */
1509 if (PointerPpe
->u
.Hard
.Valid
== 0)
1511 /* Is isn't, fault it in (make the PDE accessible) */
1512 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1516 /* Does the PDE exist? */
1517 if (PointerPde
->u
.Long
== 0)
1519 /* It does not, next range starts at the next PDE */
1520 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1524 /* Is the PDE valid? */
1525 if (PointerPde
->u
.Hard
.Valid
== 0)
1527 /* Is isn't, fault it in (make the PTE accessible) */
1528 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1531 /* We have a PTE that we can access now! */
1536 /* Is it safe to try reading the PTE? */
1539 /* FIXME: watch out for large pages */
1540 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1542 /* Capture the PTE */
1543 TempPte
= *PointerPte
;
1544 if (TempPte
.u
.Long
!= 0)
1546 /* The PTE is valid, so it's not zeroed out */
1547 DemandZeroPte
= FALSE
;
1549 /* Is it a decommited, invalid, or faulted PTE? */
1550 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1551 (TempPte
.u
.Hard
.Valid
== 0) &&
1552 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1553 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1555 /* Otherwise our defaults should hold */
1556 ASSERT(Protect
== 0);
1557 ASSERT(State
== MEM_RESERVE
);
1561 /* This means it's committed */
1564 /* We don't support these */
1565 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1566 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1567 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1569 /* Get protection state of this page */
1570 Protect
= MiGetPageProtection(PointerPte
);
1572 /* Check if this is an image-backed VAD */
1573 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1574 (TempPte
.u
.Soft
.Prototype
== 1) &&
1575 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1578 DPRINT1("Not supported\n");
1585 /* Check if this was a demand-zero PTE, since we need to find the state */
1588 /* Not yet handled */
1589 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1590 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1592 /* Check if this is private commited memory, or an section-backed VAD */
1593 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1595 /* Tell caller about the next range */
1596 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1598 /* Get the prototype PTE for this VAD */
1599 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1600 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1603 /* We should unlock the working set, but it's not being held! */
1605 /* Is the prototype PTE actually valid (committed)? */
1606 TempProtoPte
= *ProtoPte
;
1607 if (TempProtoPte
.u
.Long
)
1609 /* Unless this is a memory-mapped file, handle it like private VAD */
1611 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1612 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1615 /* We should re-lock the working set */
1618 else if (Vad
->u
.VadFlags
.MemCommit
)
1620 /* This is committed memory */
1623 /* Convert the protection */
1624 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1628 /* Return the protection code */
1629 *ReturnedProtect
= Protect
;
1635 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1636 IN PVOID BaseAddress
,
1637 OUT PVOID MemoryInformation
,
1638 IN SIZE_T MemoryInformationLength
,
1639 OUT PSIZE_T ReturnLength
)
1641 PEPROCESS TargetProcess
;
1642 NTSTATUS Status
= STATUS_SUCCESS
;
1644 PVOID Address
, NextAddress
;
1645 BOOLEAN Found
= FALSE
;
1646 ULONG NewProtect
, NewState
;
1648 MEMORY_BASIC_INFORMATION MemoryInfo
;
1649 KAPC_STATE ApcState
;
1650 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1651 PMEMORY_AREA MemoryArea
;
1652 SIZE_T ResultLength
;
1654 /* Check for illegal addresses in user-space, or the shared memory area */
1655 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1656 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1658 Address
= PAGE_ALIGN(BaseAddress
);
1660 /* Make up an info structure describing this range */
1661 MemoryInfo
.BaseAddress
= Address
;
1662 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1663 MemoryInfo
.Type
= MEM_PRIVATE
;
1665 /* Special case for shared data */
1666 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1668 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1669 MemoryInfo
.State
= MEM_COMMIT
;
1670 MemoryInfo
.Protect
= PAGE_READONLY
;
1671 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1675 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1676 MemoryInfo
.State
= MEM_RESERVE
;
1677 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1678 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1681 /* Return the data, NtQueryInformation already probed it*/
1682 if (PreviousMode
!= KernelMode
)
1686 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1687 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1691 Status
= _SEH2_GetExceptionCode();
1697 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1698 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1704 /* Check if this is for a local or remote process */
1705 if (ProcessHandle
== NtCurrentProcess())
1707 TargetProcess
= PsGetCurrentProcess();
1711 /* Reference the target process */
1712 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1713 PROCESS_QUERY_INFORMATION
,
1715 ExGetPreviousMode(),
1716 (PVOID
*)&TargetProcess
,
1718 if (!NT_SUCCESS(Status
)) return Status
;
1720 /* Attach to it now */
1721 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1724 /* Lock the address space and make sure the process isn't already dead */
1725 MmLockAddressSpace(&TargetProcess
->Vm
);
1726 if (TargetProcess
->VmDeleted
)
1728 /* Unlock the address space of the process */
1729 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1731 /* Check if we were attached */
1732 if (ProcessHandle
!= NtCurrentProcess())
1734 /* Detach and dereference the process */
1735 KeUnstackDetachProcess(&ApcState
);
1736 ObDereferenceObject(TargetProcess
);
1740 DPRINT1("Process is dying\n");
1741 return STATUS_PROCESS_IS_TERMINATING
;
1745 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1746 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1748 /* Scan on the right */
1749 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1750 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1753 /* Check if this VAD covers the allocation range */
1754 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1755 (BaseVpn
<= Vad
->EndingVpn
))
1762 /* Check if this VAD is too high */
1763 if (BaseVpn
< Vad
->StartingVpn
)
1765 /* Stop if there is no left child */
1766 if (!Vad
->LeftChild
) break;
1768 /* Search on the left next */
1769 Vad
= Vad
->LeftChild
;
1773 /* Then this VAD is too low, keep searching on the right */
1774 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1776 /* Stop if there is no right child */
1777 if (!Vad
->RightChild
) break;
1779 /* Search on the right next */
1780 Vad
= Vad
->RightChild
;
1785 /* Was a VAD found? */
1788 Address
= PAGE_ALIGN(BaseAddress
);
1790 /* Calculate region size */
1793 if (Vad
->StartingVpn
>= BaseVpn
)
1795 /* Region size is the free space till the start of that VAD */
1796 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1800 /* Get the next VAD */
1801 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1804 /* Region size is the free space till the start of that VAD */
1805 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1809 /* Maximum possible region size with that base address */
1810 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1816 /* Maximum possible region size with that base address */
1817 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1820 /* Unlock the address space of the process */
1821 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1823 /* Check if we were attached */
1824 if (ProcessHandle
!= NtCurrentProcess())
1826 /* Detach and derefernece the process */
1827 KeUnstackDetachProcess(&ApcState
);
1828 ObDereferenceObject(TargetProcess
);
1831 /* Build the rest of the initial information block */
1832 MemoryInfo
.BaseAddress
= Address
;
1833 MemoryInfo
.AllocationBase
= NULL
;
1834 MemoryInfo
.AllocationProtect
= 0;
1835 MemoryInfo
.State
= MEM_FREE
;
1836 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1837 MemoryInfo
.Type
= 0;
1839 /* Return the data, NtQueryInformation already probed it*/
1840 if (PreviousMode
!= KernelMode
)
1844 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1845 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1849 Status
= _SEH2_GetExceptionCode();
1855 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1856 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1862 /* Set the correct memory type based on what kind of VAD this is */
1863 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1864 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1866 MemoryInfo
.Type
= MEM_PRIVATE
;
1868 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1870 MemoryInfo
.Type
= MEM_IMAGE
;
1874 MemoryInfo
.Type
= MEM_MAPPED
;
1877 /* Find the memory area the specified address belongs to */
1878 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1879 ASSERT(MemoryArea
!= NULL
);
1881 /* Determine information dependent on the memory area type */
1882 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1884 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1885 if (!NT_SUCCESS(Status
))
1887 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1888 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1889 NT_ASSERT(NT_SUCCESS(Status
));
1894 /* Build the initial information block */
1895 Address
= PAGE_ALIGN(BaseAddress
);
1896 MemoryInfo
.BaseAddress
= Address
;
1897 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1898 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1899 MemoryInfo
.Type
= MEM_PRIVATE
;
1901 /* Acquire the working set lock (shared is enough) */
1902 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1904 /* Find the largest chunk of memory which has the same state and protection mask */
1905 MemoryInfo
.State
= MiQueryAddressState(Address
,
1908 &MemoryInfo
.Protect
,
1910 Address
= NextAddress
;
1911 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1913 /* Keep going unless the state or protection mask changed */
1914 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1915 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1916 Address
= NextAddress
;
1919 /* Release the working set lock */
1920 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1922 /* Check if we went outside of the VAD */
1923 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1925 /* Set the end of the VAD as the end address */
1926 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1929 /* Now that we know the last VA address, calculate the region size */
1930 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1933 /* Unlock the address space of the process */
1934 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1936 /* Check if we were attached */
1937 if (ProcessHandle
!= NtCurrentProcess())
1939 /* Detach and derefernece the process */
1940 KeUnstackDetachProcess(&ApcState
);
1941 ObDereferenceObject(TargetProcess
);
1944 /* Return the data, NtQueryInformation already probed it */
1945 if (PreviousMode
!= KernelMode
)
1949 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1950 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1952 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1954 Status
= _SEH2_GetExceptionCode();
1960 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1961 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1965 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1966 "State: %lx Type: %lx Size: %lx\n",
1967 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1968 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1969 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1976 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1977 IN ULONG_PTR EndingAddress
,
1979 IN PEPROCESS Process
)
1981 PMMPTE PointerPte
, LastPte
, PointerPde
;
1982 BOOLEAN OnBoundary
= TRUE
;
1985 /* Get the PDE and PTE addresses */
1986 PointerPde
= MiAddressToPde(StartingAddress
);
1987 PointerPte
= MiAddressToPte(StartingAddress
);
1988 LastPte
= MiAddressToPte(EndingAddress
);
1990 /* Loop all the PTEs */
1991 while (PointerPte
<= LastPte
)
1993 /* Check if we've hit an new PDE boundary */
1996 /* Is this PDE demand zero? */
1997 PointerPde
= MiAddressToPte(PointerPte
);
1998 if (PointerPde
->u
.Long
!= 0)
2000 /* It isn't -- is it valid? */
2001 if (PointerPde
->u
.Hard
.Valid
== 0)
2003 /* Nope, fault it in */
2004 PointerPte
= MiPteToAddress(PointerPde
);
2005 MiMakeSystemAddressValid(PointerPte
, Process
);
2010 /* The PTE was already valid, so move to the next one */
2012 PointerPte
= MiPteToAddress(PointerPde
);
2014 /* Is the entire VAD committed? If not, fail */
2015 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2017 /* Everything is committed so far past the range, return true */
2018 if (PointerPte
> LastPte
) return TRUE
;
2022 /* Is the PTE demand zero? */
2023 if (PointerPte
->u
.Long
== 0)
2025 /* Is the entire VAD committed? If not, fail */
2026 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2030 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2031 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
2032 (PointerPte
->u
.Hard
.Valid
== 0) &&
2033 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
2034 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
2036 /* Then part of the range is decommitted, so fail */
2041 /* Move to the next PTE */
2043 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2046 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2052 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2053 IN OUT PVOID
*BaseAddress
,
2054 IN OUT PSIZE_T NumberOfBytesToProtect
,
2055 IN ULONG NewAccessProtection
,
2056 OUT PULONG OldAccessProtection OPTIONAL
)
2058 PMEMORY_AREA MemoryArea
;
2059 PMMSUPPORT AddressSpace
;
2060 ULONG OldAccessProtection_
;
2063 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2064 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2066 AddressSpace
= &Process
->Vm
;
2067 MmLockAddressSpace(AddressSpace
);
2068 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2069 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2071 MmUnlockAddressSpace(AddressSpace
);
2072 return STATUS_UNSUCCESSFUL
;
2075 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2077 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2078 Status
= MmProtectSectionView(AddressSpace
,
2081 *NumberOfBytesToProtect
,
2082 NewAccessProtection
,
2083 OldAccessProtection
);
2085 MmUnlockAddressSpace(AddressSpace
);
2092 MiProtectVirtualMemory(IN PEPROCESS Process
,
2093 IN OUT PVOID
*BaseAddress
,
2094 IN OUT PSIZE_T NumberOfBytesToProtect
,
2095 IN ULONG NewAccessProtection
,
2096 OUT PULONG OldAccessProtection OPTIONAL
)
2098 PMEMORY_AREA MemoryArea
;
2100 PMMSUPPORT AddressSpace
;
2101 ULONG_PTR StartingAddress
, EndingAddress
;
2102 PMMPTE PointerPde
, PointerPte
, LastPte
;
2105 ULONG ProtectionMask
, OldProtect
;
2107 NTSTATUS Status
= STATUS_SUCCESS
;
2108 PETHREAD Thread
= PsGetCurrentThread();
2109 TABLE_SEARCH_RESULT Result
;
2111 /* Calculate base address for the VAD */
2112 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2113 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2115 /* Calculate the protection mask and make sure it's valid */
2116 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2117 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2119 DPRINT1("Invalid protection mask\n");
2120 return STATUS_INVALID_PAGE_PROTECTION
;
2123 /* Check for ROS specific memory area */
2124 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2125 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2128 return MiRosProtectVirtualMemory(Process
,
2130 NumberOfBytesToProtect
,
2131 NewAccessProtection
,
2132 OldAccessProtection
);
2135 /* Lock the address space and make sure the process isn't already dead */
2136 AddressSpace
= MmGetCurrentAddressSpace();
2137 MmLockAddressSpace(AddressSpace
);
2138 if (Process
->VmDeleted
)
2140 DPRINT1("Process is dying\n");
2141 Status
= STATUS_PROCESS_IS_TERMINATING
;
2145 /* Get the VAD for this address range, and make sure it exists */
2146 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2147 EndingAddress
>> PAGE_SHIFT
,
2149 (PMMADDRESS_NODE
*)&Vad
);
2150 if (Result
!= TableFoundNode
)
2152 DPRINT("Could not find a VAD for this allocation\n");
2153 Status
= STATUS_CONFLICTING_ADDRESSES
;
2157 /* Make sure the address is within this VAD's boundaries */
2158 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2159 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2161 Status
= STATUS_CONFLICTING_ADDRESSES
;
2165 /* These kinds of VADs are not supported atm */
2166 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2167 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2168 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2170 DPRINT1("Illegal VAD for attempting to set protection\n");
2171 Status
= STATUS_CONFLICTING_ADDRESSES
;
2175 /* Check for a VAD whose protection can't be changed */
2176 if (Vad
->u
.VadFlags
.NoChange
== 1)
2178 DPRINT1("Trying to change protection of a NoChange VAD\n");
2179 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2183 /* Is this section, or private memory? */
2184 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2186 /* Not yet supported */
2187 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2189 DPRINT1("Illegal VAD for attempting to set protection\n");
2190 Status
= STATUS_CONFLICTING_ADDRESSES
;
2194 /* Rotate VADs are not yet supported */
2195 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2197 DPRINT1("Illegal VAD for attempting to set protection\n");
2198 Status
= STATUS_CONFLICTING_ADDRESSES
;
2202 /* Not valid on section files */
2203 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2206 DPRINT1("Invalid protection flags for section\n");
2207 Status
= STATUS_INVALID_PARAMETER_4
;
2211 /* Check if data or page file mapping protection PTE is compatible */
2212 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2215 DPRINT1("Fixme: Not checking for valid protection\n");
2218 /* This is a section, and this is not yet supported */
2219 DPRINT1("Section protection not yet supported\n");
2224 /* Private memory, check protection flags */
2225 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2226 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2228 DPRINT1("Invalid protection flags for private memory\n");
2229 Status
= STATUS_INVALID_PARAMETER_4
;
2233 /* Lock the working set */
2234 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2236 /* Check if all pages in this range are committed */
2237 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2244 DPRINT1("The entire range is not committed\n");
2245 Status
= STATUS_NOT_COMMITTED
;
2246 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2250 /* Compute starting and ending PTE and PDE addresses */
2251 PointerPde
= MiAddressToPde(StartingAddress
);
2252 PointerPte
= MiAddressToPte(StartingAddress
);
2253 LastPte
= MiAddressToPte(EndingAddress
);
2255 /* Make this PDE valid */
2256 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2258 /* Save protection of the first page */
2259 if (PointerPte
->u
.Long
!= 0)
2261 /* Capture the page protection and make the PDE valid */
2262 OldProtect
= MiGetPageProtection(PointerPte
);
2263 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2267 /* Grab the old protection from the VAD itself */
2268 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2271 /* Loop all the PTEs now */
2272 while (PointerPte
<= LastPte
)
2274 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2275 if (MiIsPteOnPdeBoundary(PointerPte
))
2277 PointerPde
= MiAddressToPte(PointerPte
);
2278 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2281 /* Capture the PTE and check if it was empty */
2282 PteContents
= *PointerPte
;
2283 if (PteContents
.u
.Long
== 0)
2285 /* This used to be a zero PTE and it no longer is, so we must add a
2286 reference to the pagetable. */
2287 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2290 /* Check what kind of PTE we are dealing with */
2291 if (PteContents
.u
.Hard
.Valid
== 1)
2293 /* Get the PFN entry */
2294 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2296 /* We don't support this yet */
2297 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2299 /* Check if the page should not be accessible at all */
2300 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2301 (NewAccessProtection
& PAGE_GUARD
))
2303 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2305 /* Mark the PTE as transition and change its protection */
2306 PteContents
.u
.Hard
.Valid
= 0;
2307 PteContents
.u
.Soft
.Transition
= 1;
2308 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2309 /* Decrease PFN share count and write the PTE */
2310 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2311 // FIXME: remove the page from the WS
2312 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2314 // FIXME: Should invalidate entry in every CPU TLB
2317 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2319 /* We are done for this PTE */
2320 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2324 /* Write the protection mask and write it with a TLB flush */
2325 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2326 MiFlushTbAndCapture(Vad
,
2335 /* We don't support these cases yet */
2336 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2337 //ASSERT(PteContents.u.Soft.Transition == 0);
2339 /* The PTE is already demand-zero, just update the protection mask */
2340 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2341 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2342 ASSERT(PointerPte
->u
.Long
!= 0);
2345 /* Move to the next PTE */
2349 /* Unlock the working set */
2350 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2353 /* Unlock the address space */
2354 MmUnlockAddressSpace(AddressSpace
);
2356 /* Return parameters and success */
2357 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2358 *BaseAddress
= (PVOID
)StartingAddress
;
2359 *OldAccessProtection
= OldProtect
;
2360 return STATUS_SUCCESS
;
2363 /* Unlock the address space and return the failure code */
2364 MmUnlockAddressSpace(AddressSpace
);
2370 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2371 IN PEPROCESS TargetProcess
,
2374 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2377 // Sanity checks. The latter is because we only use this function with the
2378 // PFN lock not held, so it may go away in the future.
2380 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2381 ASSERT(OldIrql
== MM_NOIRQL
);
2384 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2385 // return the same address as the PDE on 2-level page table systems.
2387 // If everything is already valid, there is nothing to do.
2389 PointerPpe
= MiAddressToPte(PointerPde
);
2390 PointerPxe
= MiAddressToPde(PointerPde
);
2391 if ((PointerPxe
->u
.Hard
.Valid
) &&
2392 (PointerPpe
->u
.Hard
.Valid
) &&
2393 (PointerPde
->u
.Hard
.Valid
))
2399 // At least something is invalid, so begin by getting the PTE for the PDE itself
2400 // and then lookup each additional level. We must do it in this precise order
2401 // because the pagfault.c code (as well as in Windows) depends that the next
2402 // level up (higher) must be valid when faulting a lower level
2404 PointerPte
= MiPteToAddress(PointerPde
);
2408 // Make sure APCs continued to be disabled
2410 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2413 // First, make the PXE valid if needed
2415 if (!PointerPxe
->u
.Hard
.Valid
)
2417 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2418 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2424 if (!PointerPpe
->u
.Hard
.Valid
)
2426 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2427 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2431 // And finally, make the PDE itself valid.
2433 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2436 // This should've worked the first time so the loop is really just for
2437 // show -- ASSERT that we're actually NOT going to be looping.
2439 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2440 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2441 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2442 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2443 !(PointerPpe
->u
.Hard
.Valid
) ||
2444 !(PointerPde
->u
.Hard
.Valid
));
2449 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2455 PFN_NUMBER PageFrameIndex
;
2459 // Acquire the PFN lock and loop all the PTEs in the list
2461 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2462 for (i
= 0; i
!= Count
; i
++)
2465 // The PTE must currently be valid
2467 TempPte
= *ValidPteList
[i
];
2468 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2471 // Get the PFN entry for the page itself, and then for its page table
2473 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2474 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2475 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2478 // Decrement the share count on the page table, and then on the page
2481 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2482 MI_SET_PFN_DELETED(Pfn1
);
2483 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2486 // Make the page decommitted
2488 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2492 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2493 // and then release the PFN lock
2496 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2501 MiDecommitPages(IN PVOID StartingAddress
,
2502 IN PMMPTE EndingPte
,
2503 IN PEPROCESS Process
,
2506 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2507 ULONG CommitReduction
= 0;
2508 PMMPTE ValidPteList
[256];
2512 PETHREAD CurrentThread
= PsGetCurrentThread();
2515 // Get the PTE and PTE for the address, and lock the working set
2516 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2517 // commited range ends so that we can do the right accounting.
2519 PointerPde
= MiAddressToPde(StartingAddress
);
2520 PointerPte
= MiAddressToPte(StartingAddress
);
2521 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2522 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2525 // Make the PDE valid, and now loop through each page's worth of data
2527 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2528 while (PointerPte
<= EndingPte
)
2531 // Check if we've crossed a PDE boundary
2533 if (MiIsPteOnPdeBoundary(PointerPte
))
2536 // Get the new PDE and flush the valid PTEs we had built up until
2537 // now. This helps reduce the amount of TLB flushing we have to do.
2538 // Note that Windows does a much better job using timestamps and
2539 // such, and does not flush the entire TLB all the time, but right
2540 // now we have bigger problems to worry about than TLB flushing.
2542 PointerPde
= MiAddressToPde(StartingAddress
);
2545 MiProcessValidPteList(ValidPteList
, PteCount
);
2550 // Make this PDE valid
2552 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2556 // Read this PTE. It might be active or still demand-zero.
2558 PteContents
= *PointerPte
;
2559 if (PteContents
.u
.Long
)
2562 // The PTE is active. It might be valid and in a working set, or
2563 // it might be a prototype PTE or paged out or even in transition.
2565 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2568 // It's already decommited, so there's nothing for us to do here
2575 // Remove it from the counters, and check if it was valid or not
2577 //Process->NumberOfPrivatePages--;
2578 if (PteContents
.u
.Hard
.Valid
)
2581 // It's valid. At this point make sure that it is not a ROS
2582 // PFN. Also, we don't support ProtoPTEs in this code path.
2584 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2585 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2586 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2589 // Flush any pending PTEs that we had not yet flushed, if our
2590 // list has gotten too big, then add this PTE to the flush list.
2592 if (PteCount
== 256)
2594 MiProcessValidPteList(ValidPteList
, PteCount
);
2597 ValidPteList
[PteCount
++] = PointerPte
;
2602 // We do not support any of these other scenarios at the moment
2604 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2605 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2606 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2609 // So the only other possibility is that it is still a demand
2610 // zero PTE, in which case we undo the accounting we did
2611 // earlier and simply make the page decommitted.
2613 //Process->NumberOfPrivatePages++;
2614 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2621 // This used to be a zero PTE and it no longer is, so we must add a
2622 // reference to the pagetable.
2624 MiIncrementPageTableReferences(StartingAddress
);
2627 // Next, we account for decommitted PTEs and make the PTE as such
2629 if (PointerPte
> CommitPte
) CommitReduction
++;
2630 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2634 // Move to the next PTE and the next address
2637 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2641 // Flush any dangling PTEs from the loop in the last page table, and then
2642 // release the working set and return the commit reduction accounting.
2644 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2645 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2646 return CommitReduction
;
2649 /* PUBLIC FUNCTIONS ***********************************************************/
2656 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2667 MmSecureVirtualMemory(IN PVOID Address
,
2671 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2680 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2682 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2685 /* SYSTEM CALLS ***************************************************************/
2689 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2690 IN PVOID BaseAddress
,
2692 IN SIZE_T NumberOfBytesToRead
,
2693 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2695 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2697 NTSTATUS Status
= STATUS_SUCCESS
;
2698 SIZE_T BytesRead
= 0;
2702 // Check if we came from user mode
2704 if (PreviousMode
!= KernelMode
)
2707 // Validate the read addresses
2709 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2710 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2711 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2712 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2715 // Don't allow to write into kernel space
2717 return STATUS_ACCESS_VIOLATION
;
2721 // Enter SEH for probe
2726 // Probe the output value
2728 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2730 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2733 // Get exception code
2735 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2741 // Don't do zero-byte transfers
2743 if (NumberOfBytesToRead
)
2746 // Reference the process
2748 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2754 if (NT_SUCCESS(Status
))
2759 Status
= MmCopyVirtualMemory(Process
,
2761 PsGetCurrentProcess(),
2763 NumberOfBytesToRead
,
2768 // Dereference the process
2770 ObDereferenceObject(Process
);
2775 // Check if the caller sent this parameter
2777 if (NumberOfBytesRead
)
2780 // Enter SEH to guard write
2785 // Return the number of bytes read
2787 *NumberOfBytesRead
= BytesRead
;
2789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2803 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2804 IN PVOID BaseAddress
,
2806 IN SIZE_T NumberOfBytesToWrite
,
2807 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2809 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2811 NTSTATUS Status
= STATUS_SUCCESS
;
2812 SIZE_T BytesWritten
= 0;
2816 // Check if we came from user mode
2818 if (PreviousMode
!= KernelMode
)
2821 // Validate the read addresses
2823 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2824 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2825 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2826 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2829 // Don't allow to write into kernel space
2831 return STATUS_ACCESS_VIOLATION
;
2835 // Enter SEH for probe
2840 // Probe the output value
2842 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2844 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2847 // Get exception code
2849 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2855 // Don't do zero-byte transfers
2857 if (NumberOfBytesToWrite
)
2860 // Reference the process
2862 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2868 if (NT_SUCCESS(Status
))
2873 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2877 NumberOfBytesToWrite
,
2882 // Dereference the process
2884 ObDereferenceObject(Process
);
2889 // Check if the caller sent this parameter
2891 if (NumberOfBytesWritten
)
2894 // Enter SEH to guard write
2899 // Return the number of bytes written
2901 *NumberOfBytesWritten
= BytesWritten
;
2903 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2917 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2918 IN OUT PVOID
*UnsafeBaseAddress
,
2919 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2920 IN ULONG NewAccessProtection
,
2921 OUT PULONG UnsafeOldAccessProtection
)
2924 ULONG OldAccessProtection
;
2926 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2927 PVOID BaseAddress
= NULL
;
2928 SIZE_T NumberOfBytesToProtect
= 0;
2929 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2931 BOOLEAN Attached
= FALSE
;
2932 KAPC_STATE ApcState
;
2936 // Check for valid protection flags
2938 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2939 if (Protection
!= PAGE_NOACCESS
&&
2940 Protection
!= PAGE_READONLY
&&
2941 Protection
!= PAGE_READWRITE
&&
2942 Protection
!= PAGE_WRITECOPY
&&
2943 Protection
!= PAGE_EXECUTE
&&
2944 Protection
!= PAGE_EXECUTE_READ
&&
2945 Protection
!= PAGE_EXECUTE_READWRITE
&&
2946 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2951 return STATUS_INVALID_PAGE_PROTECTION
;
2955 // Check if we came from user mode
2957 if (PreviousMode
!= KernelMode
)
2960 // Enter SEH for probing
2965 // Validate all outputs
2967 ProbeForWritePointer(UnsafeBaseAddress
);
2968 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2969 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2974 BaseAddress
= *UnsafeBaseAddress
;
2975 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2977 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2980 // Get exception code
2982 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2991 BaseAddress
= *UnsafeBaseAddress
;
2992 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2996 // Catch illegal base address
2998 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3001 // Catch illegal region size
3003 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
3008 return STATUS_INVALID_PARAMETER_3
;
3012 // 0 is also illegal
3014 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
3017 // Get a reference to the process
3019 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3020 PROCESS_VM_OPERATION
,
3025 if (!NT_SUCCESS(Status
)) return Status
;
3028 // Check if we should attach
3030 if (CurrentProcess
!= Process
)
3035 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3040 // Do the actual work
3042 Status
= MiProtectVirtualMemory(Process
,
3044 &NumberOfBytesToProtect
,
3045 NewAccessProtection
,
3046 &OldAccessProtection
);
3051 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3054 // Release reference
3056 ObDereferenceObject(Process
);
3059 // Enter SEH to return data
3064 // Return data to user
3066 *UnsafeOldAccessProtection
= OldAccessProtection
;
3067 *UnsafeBaseAddress
= BaseAddress
;
3068 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3070 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3087 // HACK until we have proper WSLIST support
3088 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3090 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3092 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3104 // HACK until we have proper WSLIST support
3105 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3107 if (!Wsle
->u1
.e1
.LockedInWs
&&
3108 !Wsle
->u1
.e1
.LockedInMemory
)
3110 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3113 if (LockType
& MAP_PROCESS
)
3114 Wsle
->u1
.e1
.LockedInWs
= 1;
3115 if (LockType
& MAP_SYSTEM
)
3116 Wsle
->u1
.e1
.LockedInMemory
= 1;
3125 // HACK until we have proper WSLIST support
3126 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3128 if (LockType
& MAP_PROCESS
)
3129 Wsle
->u1
.e1
.LockedInWs
= 0;
3130 if (LockType
& MAP_SYSTEM
)
3131 Wsle
->u1
.e1
.LockedInMemory
= 0;
3133 if (!Wsle
->u1
.e1
.LockedInWs
&&
3134 !Wsle
->u1
.e1
.LockedInMemory
)
3136 MiDereferencePfnAndDropLockCount(Pfn1
);
3142 MiCheckVadsForLockOperation(
3143 _Inout_ PVOID
*BaseAddress
,
3144 _Inout_ PSIZE_T RegionSize
,
3145 _Inout_ PVOID
*EndAddress
)
3151 /* Get the base address and align the start address */
3152 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3153 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3154 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3156 /* First loop and check all VADs */
3157 CurrentVa
= *BaseAddress
;
3158 while (CurrentVa
< *EndAddress
)
3161 Vad
= MiLocateAddress(CurrentVa
);
3164 /// FIXME: this might be a memory area for a section view...
3165 return STATUS_ACCESS_VIOLATION
;
3168 /* Check VAD type */
3169 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3170 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3171 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3173 *EndAddress
= CurrentVa
;
3174 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3175 return STATUS_INCOMPATIBLE_FILE_MAP
;
3178 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3181 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3182 return STATUS_SUCCESS
;
3187 MiLockVirtualMemory(
3188 IN OUT PVOID
*BaseAddress
,
3189 IN OUT PSIZE_T RegionSize
,
3192 PEPROCESS CurrentProcess
;
3193 PMMSUPPORT AddressSpace
;
3194 PVOID CurrentVa
, EndAddress
;
3195 PMMPTE PointerPte
, LastPte
;
3197 #if (_MI_PAGING_LEVELS >= 3)
3200 #if (_MI_PAGING_LEVELS == 4)
3204 NTSTATUS Status
, TempStatus
;
3206 /* Lock the address space */
3207 AddressSpace
= MmGetCurrentAddressSpace();
3208 MmLockAddressSpace(AddressSpace
);
3210 /* Make sure we still have an address space */
3211 CurrentProcess
= PsGetCurrentProcess();
3212 if (CurrentProcess
->VmDeleted
)
3214 Status
= STATUS_PROCESS_IS_TERMINATING
;
3218 /* Check the VADs in the requested range */
3219 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3220 if (!NT_SUCCESS(Status
))
3225 /* Enter SEH for probing */
3228 /* Loop all pages and probe them */
3229 CurrentVa
= *BaseAddress
;
3230 while (CurrentVa
< EndAddress
)
3232 (void)(*(volatile CHAR
*)CurrentVa
);
3233 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3236 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3238 Status
= _SEH2_GetExceptionCode();
3243 /* All pages were accessible, since we hold the address space lock, nothing
3244 can be de-committed. Assume success for now. */
3245 Status
= STATUS_SUCCESS
;
3247 /* Get the PTE and PDE */
3248 PointerPte
= MiAddressToPte(*BaseAddress
);
3249 PointerPde
= MiAddressToPde(*BaseAddress
);
3250 #if (_MI_PAGING_LEVELS >= 3)
3251 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3253 #if (_MI_PAGING_LEVELS == 4)
3254 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3257 /* Get the last PTE */
3258 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3260 /* Lock the process working set */
3261 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3263 /* Loop the pages */
3266 /* Check for a page that is not accessible */
3268 #if (_MI_PAGING_LEVELS == 4)
3269 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3271 #if (_MI_PAGING_LEVELS >= 3)
3272 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3274 (PointerPde
->u
.Hard
.Valid
== 0) ||
3275 (PointerPte
->u
.Hard
.Valid
== 0))
3277 /* Release process working set */
3278 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3280 /* Access the page */
3281 CurrentVa
= MiPteToAddress(PointerPte
);
3283 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3284 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3285 if (!NT_SUCCESS(TempStatus
))
3287 // This should only happen, when remote backing storage is not accessible
3289 Status
= TempStatus
;
3293 /* Lock the process working set */
3294 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3298 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3299 ASSERT(Pfn1
!= NULL
);
3301 /* Check the previous lock status */
3302 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3304 Status
= STATUS_WAS_LOCKED
;
3308 MI_LOCK_VA(Pfn1
, MapType
);
3310 /* Go to the next PTE */
3313 /* Check if we're on a PDE boundary */
3314 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3315 #if (_MI_PAGING_LEVELS >= 3)
3316 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3318 #if (_MI_PAGING_LEVELS == 4)
3319 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3321 } while (PointerPte
<= LastPte
);
3323 /* Release process working set */
3324 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3327 /* Unlock address space */
3328 MmUnlockAddressSpace(AddressSpace
);
3335 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3336 IN OUT PVOID
*BaseAddress
,
3337 IN OUT PSIZE_T NumberOfBytesToLock
,
3341 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3343 BOOLEAN Attached
= FALSE
;
3344 KAPC_STATE ApcState
;
3345 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3346 PVOID CapturedBaseAddress
;
3347 SIZE_T CapturedBytesToLock
;
3353 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3356 // Invalid set of flags
3358 return STATUS_INVALID_PARAMETER
;
3362 // At least one flag must be specified
3364 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3369 return STATUS_INVALID_PARAMETER
;
3373 // Enter SEH for probing
3378 // Validate output data
3380 ProbeForWritePointer(BaseAddress
);
3381 ProbeForWriteSize_t(NumberOfBytesToLock
);
3386 CapturedBaseAddress
= *BaseAddress
;
3387 CapturedBytesToLock
= *NumberOfBytesToLock
;
3389 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3392 // Get exception code
3394 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3399 // Catch illegal base address
3401 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3404 // Catch illegal region size
3406 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3411 return STATUS_INVALID_PARAMETER
;
3415 // 0 is also illegal
3417 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3420 // Get a reference to the process
3422 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3423 PROCESS_VM_OPERATION
,
3428 if (!NT_SUCCESS(Status
)) return Status
;
3431 // Check if this is is system-mapped
3433 if (MapType
& MAP_SYSTEM
)
3436 // Check for required privilege
3438 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3441 // Fail: Don't have it
3443 ObDereferenceObject(Process
);
3444 return STATUS_PRIVILEGE_NOT_HELD
;
3449 // Check if we should attach
3451 if (CurrentProcess
!= Process
)
3456 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3461 // Call the internal function
3463 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3464 &CapturedBytesToLock
,
3470 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3473 // Release reference
3475 ObDereferenceObject(Process
);
3478 // Enter SEH to return data
3483 // Return data to user
3485 *BaseAddress
= CapturedBaseAddress
;
3486 *NumberOfBytesToLock
= CapturedBytesToLock
;
3488 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3491 // Get exception code
3493 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3506 MiUnlockVirtualMemory(
3507 IN OUT PVOID
*BaseAddress
,
3508 IN OUT PSIZE_T RegionSize
,
3511 PEPROCESS CurrentProcess
;
3512 PMMSUPPORT AddressSpace
;
3514 PMMPTE PointerPte
, LastPte
;
3516 #if (_MI_PAGING_LEVELS >= 3)
3519 #if (_MI_PAGING_LEVELS == 4)
3525 /* Lock the address space */
3526 AddressSpace
= MmGetCurrentAddressSpace();
3527 MmLockAddressSpace(AddressSpace
);
3529 /* Make sure we still have an address space */
3530 CurrentProcess
= PsGetCurrentProcess();
3531 if (CurrentProcess
->VmDeleted
)
3533 Status
= STATUS_PROCESS_IS_TERMINATING
;
3537 /* Check the VADs in the requested range */
3538 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3540 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3541 incompatible VAD we continue, like Windows does */
3542 if (Status
== STATUS_ACCESS_VIOLATION
)
3544 Status
= STATUS_NOT_LOCKED
;
3548 /* Get the PTE and PDE */
3549 PointerPte
= MiAddressToPte(*BaseAddress
);
3550 PointerPde
= MiAddressToPde(*BaseAddress
);
3551 #if (_MI_PAGING_LEVELS >= 3)
3552 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3554 #if (_MI_PAGING_LEVELS == 4)
3555 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3558 /* Get the last PTE */
3559 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3561 /* Lock the process working set */
3562 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3564 /* Loop the pages */
3567 /* Check for a page that is not present */
3569 #if (_MI_PAGING_LEVELS == 4)
3570 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3572 #if (_MI_PAGING_LEVELS >= 3)
3573 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3575 (PointerPde
->u
.Hard
.Valid
== 0) ||
3576 (PointerPte
->u
.Hard
.Valid
== 0))
3578 /* Remember it, but keep going */
3579 Status
= STATUS_NOT_LOCKED
;
3584 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3585 ASSERT(Pfn1
!= NULL
);
3587 /* Check if all of the requested locks are present */
3588 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3589 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3591 /* Remember it, but keep going */
3592 Status
= STATUS_NOT_LOCKED
;
3594 /* Check if no lock is present */
3595 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3597 DPRINT1("FIXME: Should remove the page from WS\n");
3602 /* Go to the next PTE */
3605 /* Check if we're on a PDE boundary */
3606 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3607 #if (_MI_PAGING_LEVELS >= 3)
3608 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3610 #if (_MI_PAGING_LEVELS == 4)
3611 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3613 } while (PointerPte
<= LastPte
);
3615 /* Check if we hit a page that was not locked */
3616 if (Status
== STATUS_NOT_LOCKED
)
3618 goto CleanupWithWsLock
;
3621 /* All pages in the region were locked, so unlock them all */
3623 /* Get the PTE and PDE */
3624 PointerPte
= MiAddressToPte(*BaseAddress
);
3625 PointerPde
= MiAddressToPde(*BaseAddress
);
3626 #if (_MI_PAGING_LEVELS >= 3)
3627 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3629 #if (_MI_PAGING_LEVELS == 4)
3630 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3633 /* Loop the pages */
3637 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3638 MI_UNLOCK_VA(Pfn1
, MapType
);
3640 /* Go to the next PTE */
3643 /* Check if we're on a PDE boundary */
3644 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3645 #if (_MI_PAGING_LEVELS >= 3)
3646 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3648 #if (_MI_PAGING_LEVELS == 4)
3649 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3651 } while (PointerPte
<= LastPte
);
3653 /* Everything is done */
3654 Status
= STATUS_SUCCESS
;
3658 /* Release process working set */
3659 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3662 /* Unlock address space */
3663 MmUnlockAddressSpace(AddressSpace
);
3671 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3672 IN OUT PVOID
*BaseAddress
,
3673 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3677 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3679 BOOLEAN Attached
= FALSE
;
3680 KAPC_STATE ApcState
;
3681 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3682 PVOID CapturedBaseAddress
;
3683 SIZE_T CapturedBytesToUnlock
;
3689 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3692 // Invalid set of flags
3694 return STATUS_INVALID_PARAMETER
;
3698 // At least one flag must be specified
3700 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3705 return STATUS_INVALID_PARAMETER
;
3709 // Enter SEH for probing
3714 // Validate output data
3716 ProbeForWritePointer(BaseAddress
);
3717 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3722 CapturedBaseAddress
= *BaseAddress
;
3723 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3725 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3728 // Get exception code
3730 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3735 // Catch illegal base address
3737 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3740 // Catch illegal region size
3742 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3747 return STATUS_INVALID_PARAMETER
;
3751 // 0 is also illegal
3753 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3756 // Get a reference to the process
3758 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3759 PROCESS_VM_OPERATION
,
3764 if (!NT_SUCCESS(Status
)) return Status
;
3767 // Check if this is is system-mapped
3769 if (MapType
& MAP_SYSTEM
)
3772 // Check for required privilege
3774 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3777 // Fail: Don't have it
3779 ObDereferenceObject(Process
);
3780 return STATUS_PRIVILEGE_NOT_HELD
;
3785 // Check if we should attach
3787 if (CurrentProcess
!= Process
)
3792 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3797 // Call the internal function
3799 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3800 &CapturedBytesToUnlock
,
3806 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3809 // Release reference
3811 ObDereferenceObject(Process
);
3814 // Enter SEH to return data
3819 // Return data to user
3821 *BaseAddress
= CapturedBaseAddress
;
3822 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3824 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3827 // Get exception code
3829 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3836 return STATUS_SUCCESS
;
3841 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3842 IN OUT PVOID
*BaseAddress
,
3843 IN OUT PSIZE_T NumberOfBytesToFlush
,
3844 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3848 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3849 PVOID CapturedBaseAddress
;
3850 SIZE_T CapturedBytesToFlush
;
3851 IO_STATUS_BLOCK LocalStatusBlock
;
3855 // Check if we came from user mode
3857 if (PreviousMode
!= KernelMode
)
3860 // Enter SEH for probing
3865 // Validate all outputs
3867 ProbeForWritePointer(BaseAddress
);
3868 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3869 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3874 CapturedBaseAddress
= *BaseAddress
;
3875 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3877 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3880 // Get exception code
3882 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3891 CapturedBaseAddress
= *BaseAddress
;
3892 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3896 // Catch illegal base address
3898 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3901 // Catch illegal region size
3903 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3908 return STATUS_INVALID_PARAMETER
;
3912 // Get a reference to the process
3914 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3915 PROCESS_VM_OPERATION
,
3920 if (!NT_SUCCESS(Status
)) return Status
;
3925 Status
= MmFlushVirtualMemory(Process
,
3926 &CapturedBaseAddress
,
3927 &CapturedBytesToFlush
,
3931 // Release reference
3933 ObDereferenceObject(Process
);
3936 // Enter SEH to return data
3941 // Return data to user
3943 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3944 *NumberOfBytesToFlush
= 0;
3945 *IoStatusBlock
= LocalStatusBlock
;
3947 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3963 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3965 IN PVOID BaseAddress
,
3966 IN SIZE_T RegionSize
,
3967 IN PVOID
*UserAddressArray
,
3968 OUT PULONG_PTR EntriesInUserAddressArray
,
3969 OUT PULONG Granularity
)
3974 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3975 ULONG_PTR CapturedEntryCount
;
3979 // Check if we came from user mode
3981 if (PreviousMode
!= KernelMode
)
3984 // Enter SEH for probing
3989 // Catch illegal base address
3991 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3994 // Catch illegal region size
3996 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4001 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
4005 // Validate all data
4007 ProbeForWriteSize_t(EntriesInUserAddressArray
);
4008 ProbeForWriteUlong(Granularity
);
4013 CapturedEntryCount
= *EntriesInUserAddressArray
;
4016 // Must have a count
4018 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4021 // Can't be larger than the maximum
4023 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
4028 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4032 // Probe the actual array
4034 ProbeForWrite(UserAddressArray
,
4035 CapturedEntryCount
* sizeof(PVOID
),
4038 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4041 // Get exception code
4043 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4052 CapturedEntryCount
= *EntriesInUserAddressArray
;
4053 ASSERT(CapturedEntryCount
!= 0);
4057 // Check if this is a local request
4059 if (ProcessHandle
== NtCurrentProcess())
4062 // No need to reference the process
4064 Process
= PsGetCurrentProcess();
4069 // Reference the target
4071 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4072 PROCESS_VM_OPERATION
,
4077 if (!NT_SUCCESS(Status
)) return Status
;
4081 // Compute the last address and validate it
4083 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4084 if (BaseAddress
> EndAddress
)
4089 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4090 return STATUS_INVALID_PARAMETER_4
;
4099 // Dereference if needed
4101 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4104 // Enter SEH to return data
4109 // Return data to user
4111 *EntriesInUserAddressArray
= 0;
4112 *Granularity
= PAGE_SIZE
;
4114 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4117 // Get exception code
4119 Status
= _SEH2_GetExceptionCode();
4126 return STATUS_SUCCESS
;
4134 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4135 IN PVOID BaseAddress
,
4136 IN SIZE_T RegionSize
)
4141 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4142 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4145 // Catch illegal base address
4147 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4150 // Catch illegal region size
4152 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4157 return STATUS_INVALID_PARAMETER_3
;
4161 // Check if this is a local request
4163 if (ProcessHandle
== NtCurrentProcess())
4166 // No need to reference the process
4168 Process
= PsGetCurrentProcess();
4173 // Reference the target
4175 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4176 PROCESS_VM_OPERATION
,
4181 if (!NT_SUCCESS(Status
)) return Status
;
4185 // Compute the last address and validate it
4187 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4188 if (BaseAddress
> EndAddress
)
4193 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4194 return STATUS_INVALID_PARAMETER_3
;
4203 // Dereference if needed
4205 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4210 return STATUS_SUCCESS
;
4215 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4216 IN PVOID BaseAddress
,
4217 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4218 OUT PVOID MemoryInformation
,
4219 IN SIZE_T MemoryInformationLength
,
4220 OUT PSIZE_T ReturnLength
)
4222 NTSTATUS Status
= STATUS_SUCCESS
;
4223 KPROCESSOR_MODE PreviousMode
;
4225 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4227 /* Bail out if the address is invalid */
4228 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4230 /* Probe return buffer */
4231 PreviousMode
= ExGetPreviousMode();
4232 if (PreviousMode
!= KernelMode
)
4236 ProbeForWrite(MemoryInformation
,
4237 MemoryInformationLength
,
4240 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4242 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4244 Status
= _SEH2_GetExceptionCode();
4248 if (!NT_SUCCESS(Status
))
4254 switch(MemoryInformationClass
)
4256 case MemoryBasicInformation
:
4257 /* Validate the size information of the class */
4258 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4260 /* The size is invalid */
4261 return STATUS_INFO_LENGTH_MISMATCH
;
4263 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4266 MemoryInformationLength
,
4270 case MemorySectionName
:
4271 /* Validate the size information of the class */
4272 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4274 /* The size is invalid */
4275 return STATUS_INFO_LENGTH_MISMATCH
;
4277 Status
= MiQueryMemorySectionName(ProcessHandle
,
4280 MemoryInformationLength
,
4283 case MemoryWorkingSetList
:
4284 case MemoryBasicVlmInformation
:
4286 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4298 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4299 IN OUT PVOID
* UBaseAddress
,
4300 IN ULONG_PTR ZeroBits
,
4301 IN OUT PSIZE_T URegionSize
,
4302 IN ULONG AllocationType
,
4306 PMEMORY_AREA MemoryArea
;
4307 PFN_NUMBER PageCount
;
4308 PMMVAD Vad
= NULL
, FoundVad
;
4310 PMMSUPPORT AddressSpace
;
4312 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
, HighestAddress
;
4313 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4314 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4315 PETHREAD CurrentThread
= PsGetCurrentThread();
4316 KAPC_STATE ApcState
;
4317 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4318 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4320 PMMPTE PointerPte
, PointerPde
, LastPte
;
4321 TABLE_SEARCH_RESULT Result
;
4322 PMMADDRESS_NODE Parent
;
4325 /* Check for valid Zero bits */
4326 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4328 DPRINT1("Too many zero bits\n");
4329 return STATUS_INVALID_PARAMETER_3
;
4332 /* Check for valid Allocation Types */
4333 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4334 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4336 DPRINT1("Invalid Allocation Type\n");
4337 return STATUS_INVALID_PARAMETER_5
;
4340 /* Check for at least one of these Allocation Types to be set */
4341 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4343 DPRINT1("No memory allocation base type\n");
4344 return STATUS_INVALID_PARAMETER_5
;
4347 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4348 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4350 DPRINT1("Invalid use of MEM_RESET\n");
4351 return STATUS_INVALID_PARAMETER_5
;
4354 /* Check if large pages are being used */
4355 if (AllocationType
& MEM_LARGE_PAGES
)
4357 /* Large page allocations MUST be committed */
4358 if (!(AllocationType
& MEM_COMMIT
))
4360 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4361 return STATUS_INVALID_PARAMETER_5
;
4364 /* These flags are not allowed with large page allocations */
4365 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4367 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4368 return STATUS_INVALID_PARAMETER_5
;
4372 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4373 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4375 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4376 return STATUS_INVALID_PARAMETER_5
;
4379 /* Check for valid MEM_PHYSICAL usage */
4380 if (AllocationType
& MEM_PHYSICAL
)
4382 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4383 if (!(AllocationType
& MEM_RESERVE
))
4385 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4386 return STATUS_INVALID_PARAMETER_5
;
4389 /* Only these flags are allowed with MEM_PHYSIAL */
4390 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4392 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4393 return STATUS_INVALID_PARAMETER_5
;
4396 /* Then make sure PAGE_READWRITE is used */
4397 if (Protect
!= PAGE_READWRITE
)
4399 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4400 return STATUS_INVALID_PARAMETER_6
;
4404 /* Calculate the protection mask and make sure it's valid */
4405 ProtectionMask
= MiMakeProtectionMask(Protect
);
4406 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4408 DPRINT1("Invalid protection mask\n");
4409 return STATUS_INVALID_PAGE_PROTECTION
;
4415 /* Check for user-mode parameters */
4416 if (PreviousMode
!= KernelMode
)
4418 /* Make sure they are writable */
4419 ProbeForWritePointer(UBaseAddress
);
4420 ProbeForWriteSize_t(URegionSize
);
4423 /* Capture their values */
4424 PBaseAddress
= *UBaseAddress
;
4425 PRegionSize
= *URegionSize
;
4427 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4429 /* Return the exception code */
4430 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4434 /* Make sure the allocation isn't past the VAD area */
4435 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4437 DPRINT1("Virtual allocation base above User Space\n");
4438 return STATUS_INVALID_PARAMETER_2
;
4441 /* Make sure the allocation wouldn't overflow past the VAD area */
4442 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4444 DPRINT1("Region size would overflow into kernel-memory\n");
4445 return STATUS_INVALID_PARAMETER_4
;
4448 /* Make sure there's a size specified */
4451 DPRINT1("Region size is invalid (zero)\n");
4452 return STATUS_INVALID_PARAMETER_4
;
4456 // If this is for the current process, just use PsGetCurrentProcess
4458 if (ProcessHandle
== NtCurrentProcess())
4460 Process
= CurrentProcess
;
4465 // Otherwise, reference the process with VM rights and attach to it if
4466 // this isn't the current process. We must attach because we'll be touching
4467 // PTEs and PDEs that belong to user-mode memory, and also touching the
4468 // Working Set which is stored in Hyperspace.
4470 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4471 PROCESS_VM_OPERATION
,
4476 if (!NT_SUCCESS(Status
)) return Status
;
4477 if (CurrentProcess
!= Process
)
4479 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4484 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4485 Process
, PBaseAddress
, ZeroBits
, PRegionSize
, AllocationType
, Protect
);
4488 // Check for large page allocations and make sure that the required privilege
4489 // is being held, before attempting to handle them.
4491 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4492 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4494 /* Fail without it */
4495 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4496 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4497 goto FailPathNoLock
;
4501 // Fail on the things we don't yet support
4503 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4505 DPRINT1("MEM_LARGE_PAGES not supported\n");
4506 Status
= STATUS_INVALID_PARAMETER
;
4507 goto FailPathNoLock
;
4509 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4511 DPRINT1("MEM_PHYSICAL not supported\n");
4512 Status
= STATUS_INVALID_PARAMETER
;
4513 goto FailPathNoLock
;
4515 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4517 DPRINT1("MEM_WRITE_WATCH not supported\n");
4518 Status
= STATUS_INVALID_PARAMETER
;
4519 goto FailPathNoLock
;
4523 // Check if the caller is reserving memory, or committing memory and letting
4524 // us pick the base address
4526 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4529 // Do not allow COPY_ON_WRITE through this API
4531 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4533 DPRINT1("Copy on write not allowed through this path\n");
4534 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4535 goto FailPathNoLock
;
4539 // Does the caller have an address in mind, or is this a blind commit?
4544 // This is a blind commit, all we need is the region size
4546 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4547 PageCount
= BYTES_TO_PAGES(PRegionSize
);
4549 StartingAddress
= 0;
4552 // Check if ZeroBits were specified
4557 // Calculate the highest address and check if it's valid
4559 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4560 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4562 Status
= STATUS_INVALID_PARAMETER_3
;
4563 goto FailPathNoLock
;
4569 // Use the highest VAD address as maximum
4571 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4577 // This is a reservation, so compute the starting address on the
4578 // expected 64KB granularity, and see where the ending address will
4579 // fall based on the aligned address and the passed in region size
4581 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4582 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4583 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
4587 // Allocate and initialize the VAD
4589 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4592 DPRINT1("Failed to allocate a VAD!\n");
4593 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4594 goto FailPathNoLock
;
4597 Vad
->u
.LongFlags
= 0;
4598 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4599 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4600 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4601 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
4604 // Lock the address space and make sure the process isn't already dead
4606 AddressSpace
= MmGetCurrentAddressSpace();
4607 MmLockAddressSpace(AddressSpace
);
4608 if (Process
->VmDeleted
)
4610 Status
= STATUS_PROCESS_IS_TERMINATING
;
4615 // Did we have a base address? If no, find a valid address that is 64KB
4616 // aligned in the VAD tree. Otherwise, make sure that the address range
4617 // which was passed in isn't already conflicting with an existing address
4622 /* Which way should we search? */
4623 if ((AllocationType
& MEM_TOP_DOWN
) || Process
->VmTopDown
)
4625 /* Find an address top-down */
4626 Result
= MiFindEmptyAddressRangeDownTree(PRegionSize
,
4635 /* Find an address bottom-up */
4636 Result
= MiFindEmptyAddressRangeInTree(PRegionSize
,
4643 if (Result
== TableFoundNode
)
4645 Status
= STATUS_NO_MEMORY
;
4650 // Now we know where the allocation ends. Make sure it doesn't end up
4651 // somewhere in kernel mode.
4653 ASSERT(StartingAddress
!= 0);
4654 ASSERT(StartingAddress
< (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
);
4655 EndingAddress
= (StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4656 ASSERT(EndingAddress
> StartingAddress
);
4657 if (EndingAddress
> HighestAddress
)
4659 Status
= STATUS_NO_MEMORY
;
4665 /* Make sure it doesn't conflict with an existing allocation */
4666 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4667 EndingAddress
>> PAGE_SHIFT
,
4670 if (Result
== TableFoundNode
)
4673 // The address specified is in conflict!
4675 Status
= STATUS_CONFLICTING_ADDRESSES
;
4681 // Write out the VAD fields for this allocation
4683 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
4684 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
4687 // FIXME: Should setup VAD bitmap
4689 Status
= STATUS_SUCCESS
;
4692 // Lock the working set and insert the VAD into the process VAD tree
4694 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4695 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4696 Process
->VadRoot
.NodeHint
= Vad
;
4697 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
4698 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4701 // Make sure the actual region size is at least as big as the
4702 // requested region size and update the value
4704 ASSERT(PRegionSize
<= (EndingAddress
+ 1 - StartingAddress
));
4705 PRegionSize
= (EndingAddress
+ 1 - StartingAddress
);
4708 // Update the virtual size of the process, and if this is now the highest
4709 // virtual size we have ever seen, update the peak virtual size to reflect
4712 Process
->VirtualSize
+= PRegionSize
;
4713 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4715 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4719 // Release address space and detach and dereference the target process if
4720 // it was different from the current process
4722 MmUnlockAddressSpace(AddressSpace
);
4723 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4724 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4727 // Use SEH to write back the base address and the region size. In the case
4728 // of an exception, we do not return back the exception code, as the memory
4729 // *has* been allocated. The caller would now have to call VirtualQuery
4730 // or do some other similar trick to actually find out where its memory
4731 // allocation ended up
4735 *URegionSize
= PRegionSize
;
4736 *UBaseAddress
= (PVOID
)StartingAddress
;
4738 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4741 // Ignore exception!
4745 DPRINT("Reserved %x bytes at %p.\n", PRegionSize
, StartingAddress
);
4746 return STATUS_SUCCESS
;
4750 // This is a MEM_COMMIT on top of an existing address which must have been
4751 // MEM_RESERVED already. Compute the start and ending base addresses based
4752 // on the user input, and then compute the actual region size once all the
4753 // alignments have been done.
4755 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4756 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4757 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4760 // Lock the address space and make sure the process isn't already dead
4762 AddressSpace
= MmGetCurrentAddressSpace();
4763 MmLockAddressSpace(AddressSpace
);
4764 if (Process
->VmDeleted
)
4766 DPRINT1("Process is dying\n");
4767 Status
= STATUS_PROCESS_IS_TERMINATING
;
4772 // Get the VAD for this address range, and make sure it exists
4774 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4775 EndingAddress
>> PAGE_SHIFT
,
4777 (PMMADDRESS_NODE
*)&FoundVad
);
4778 if (Result
!= TableFoundNode
)
4780 DPRINT1("Could not find a VAD for this allocation\n");
4781 Status
= STATUS_CONFLICTING_ADDRESSES
;
4785 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4787 /// @todo HACK: pretend success
4788 DPRINT("MEM_RESET not supported\n");
4789 Status
= STATUS_SUCCESS
;
4794 // These kinds of VADs are illegal for this Windows function when trying to
4795 // commit an existing range
4797 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4798 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4799 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4801 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4802 Status
= STATUS_CONFLICTING_ADDRESSES
;
4807 // Make sure that this address range actually fits within the VAD for it
4809 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4810 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4812 DPRINT1("Address range does not fit into the VAD\n");
4813 Status
= STATUS_CONFLICTING_ADDRESSES
;
4818 // Make sure this is an ARM3 section
4820 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4821 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4823 DPRINT1("Illegal commit of non-ARM3 section!\n");
4824 Status
= STATUS_ALREADY_COMMITTED
;
4828 // Is this a previously reserved section being committed? If so, enter the
4829 // special section path
4831 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4834 // You cannot commit large page sections through this API
4836 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4838 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4839 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4844 // You can only use caching flags on a rotate VAD
4846 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4847 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4849 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4850 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4855 // We should make sure that the section's permissions aren't being
4858 if (FoundVad
->u
.VadFlags
.NoChange
)
4861 // Make sure it's okay to touch it
4862 // Note: The Windows 2003 kernel has a bug here, passing the
4863 // unaligned base address together with the aligned size,
4864 // potentially covering a region larger than the actual allocation.
4865 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4866 // For now we keep this behavior.
4867 // TODO: analyze possible implications, create test case
4869 Status
= MiCheckSecuredVad(FoundVad
,
4873 if (!NT_SUCCESS(Status
))
4875 DPRINT1("Secured VAD being messed around with\n");
4881 // ARM3 does not support file-backed sections, only shared memory
4883 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4886 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4888 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4889 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4891 DPRINT1("Invalid page protection for rotate VAD\n");
4892 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4897 // Compute PTE addresses and the quota charge, then grab the commit lock
4899 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4900 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4901 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4902 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4905 // Get the segment template PTE and start looping each page
4907 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4908 ASSERT(TempPte
.u
.Long
!= 0);
4909 while (PointerPte
<= LastPte
)
4912 // For each non-already-committed page, write the invalid template PTE
4914 if (PointerPte
->u
.Long
== 0)
4916 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4926 // Now do the commit accounting and release the lock
4928 ASSERT(QuotaCharge
>= QuotaFree
);
4929 QuotaCharge
-= QuotaFree
;
4930 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4931 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4934 // We are done with committing the section pages
4936 Status
= STATUS_SUCCESS
;
4941 // This is a specific ReactOS check because we only use normal VADs
4943 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4946 // While this is an actual Windows check
4948 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4951 // Throw out attempts to use copy-on-write through this API path
4953 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4955 DPRINT1("Write copy attempted when not allowed\n");
4956 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4961 // Initialize a demand-zero PTE
4964 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4965 NT_ASSERT(TempPte
.u
.Long
!= 0);
4968 // Get the PTE, PDE and the last PTE for this address range
4970 PointerPde
= MiAddressToPde(StartingAddress
);
4971 PointerPte
= MiAddressToPte(StartingAddress
);
4972 LastPte
= MiAddressToPte(EndingAddress
);
4975 // Update the commit charge in the VAD as well as in the process, and check
4976 // if this commit charge was now higher than the last recorded peak, in which
4977 // case we also update the peak
4979 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4980 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4981 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4983 Process
->CommitChargePeak
= Process
->CommitCharge
;
4987 // Lock the working set while we play with user pages and page tables
4989 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4992 // Make the current page table valid, and then loop each page within it
4994 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4995 while (PointerPte
<= LastPte
)
4998 // Have we crossed into a new page table?
5000 if (MiIsPteOnPdeBoundary(PointerPte
))
5003 // Get the PDE and now make it valid too
5005 PointerPde
= MiAddressToPte(PointerPte
);
5006 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
5010 // Is this a zero PTE as expected?
5012 if (PointerPte
->u
.Long
== 0)
5015 // First increment the count of pages in the page table for this
5018 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
5021 // And now write the invalid demand-zero PTE as requested
5023 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
5025 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
5028 // If the PTE was already decommitted, there is nothing else to do
5029 // but to write the new demand-zero PTE
5031 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
5033 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
5036 // We don't handle these scenarios yet
5038 if (PointerPte
->u
.Soft
.Valid
== 0)
5040 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
5041 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
5045 // There's a change in protection, remember this for later, but do
5046 // not yet handle it.
5048 ChangeProtection
= TRUE
;
5052 // Move to the next PTE
5058 // Release the working set lock, unlock the address space, and detach from
5059 // the target process if it was not the current process. Also dereference the
5060 // target process if this wasn't the case.
5062 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5063 Status
= STATUS_SUCCESS
;
5065 MmUnlockAddressSpace(AddressSpace
);
5067 if (!NT_SUCCESS(Status
))
5071 ExFreePoolWithTag(Vad
, 'SdaV');
5076 // Check if we need to update the protection
5078 if (ChangeProtection
)
5080 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
5081 SIZE_T ProtectSize
= PRegionSize
;
5082 ULONG OldProtection
;
5085 // Change the protection of the region
5087 MiProtectVirtualMemory(Process
,
5088 &ProtectBaseAddress
,
5095 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5096 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5099 // Only write back results on success
5101 if (NT_SUCCESS(Status
))
5104 // Use SEH to write back the base address and the region size. In the case
5105 // of an exception, we strangely do return back the exception code, even
5106 // though the memory *has* been allocated. This mimics Windows behavior and
5107 // there is not much we can do about it.
5111 *URegionSize
= PRegionSize
;
5112 *UBaseAddress
= (PVOID
)StartingAddress
;
5114 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5116 Status
= _SEH2_GetExceptionCode();
5129 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5130 IN PVOID
* UBaseAddress
,
5131 IN PSIZE_T URegionSize
,
5134 PMEMORY_AREA MemoryArea
;
5137 LONG_PTR CommitReduction
= 0;
5138 ULONG_PTR StartingAddress
, EndingAddress
;
5142 PMMSUPPORT AddressSpace
;
5143 PETHREAD CurrentThread
= PsGetCurrentThread();
5144 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5145 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5146 KAPC_STATE ApcState
;
5147 BOOLEAN Attached
= FALSE
;
5151 // Only two flags are supported
5153 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5155 DPRINT1("Invalid FreeType\n");
5156 return STATUS_INVALID_PARAMETER_4
;
5160 // Check if no flag was used, or if both flags were used
5162 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5163 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5165 DPRINT1("Invalid FreeType combination\n");
5166 return STATUS_INVALID_PARAMETER_4
;
5170 // Enter SEH for probe and capture. On failure, return back to the caller
5171 // with an exception violation.
5176 // Check for user-mode parameters and make sure that they are writeable
5178 if (PreviousMode
!= KernelMode
)
5180 ProbeForWritePointer(UBaseAddress
);
5181 ProbeForWriteUlong(URegionSize
);
5185 // Capture the current values
5187 PBaseAddress
= *UBaseAddress
;
5188 PRegionSize
= *URegionSize
;
5190 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5192 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5197 // Make sure the allocation isn't past the user area
5199 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5201 DPRINT1("Virtual free base above User Space\n");
5202 return STATUS_INVALID_PARAMETER_2
;
5206 // Make sure the allocation wouldn't overflow past the user area
5208 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5210 DPRINT1("Region size would overflow into kernel-memory\n");
5211 return STATUS_INVALID_PARAMETER_3
;
5215 // If this is for the current process, just use PsGetCurrentProcess
5217 if (ProcessHandle
== NtCurrentProcess())
5219 Process
= CurrentProcess
;
5224 // Otherwise, reference the process with VM rights and attach to it if
5225 // this isn't the current process. We must attach because we'll be touching
5226 // PTEs and PDEs that belong to user-mode memory, and also touching the
5227 // Working Set which is stored in Hyperspace.
5229 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5230 PROCESS_VM_OPERATION
,
5235 if (!NT_SUCCESS(Status
)) return Status
;
5236 if (CurrentProcess
!= Process
)
5238 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5243 DPRINT("NtFreeVirtualMemory: Process 0x%p, Adress 0x%p, size 0x%x, FreeType %x.\n",
5244 Process
, PBaseAddress
, PRegionSize
, FreeType
);
5247 // Lock the address space
5249 AddressSpace
= MmGetCurrentAddressSpace();
5250 MmLockAddressSpace(AddressSpace
);
5253 // If the address space is being deleted, fail the de-allocation since it's
5254 // too late to do anything about it
5256 if (Process
->VmDeleted
)
5258 DPRINT1("Process is dead\n");
5259 Status
= STATUS_PROCESS_IS_TERMINATING
;
5264 // Compute start and end addresses, and locate the VAD
5266 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5267 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5268 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5271 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5272 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5277 // If the range exceeds the VAD's ending VPN, fail this request
5279 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5281 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5282 Status
= STATUS_UNABLE_TO_FREE_VM
;
5287 // Only private memory (except rotate VADs) can be freed through here */
5289 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5290 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5291 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5293 DPRINT1("Attempt to free section memory\n");
5294 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5299 // ARM3 does not yet handle protected VM
5301 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5304 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5305 // and that is is an ARM3 memory area, and not a section view, as we currently
5306 // don't support freeing those though this interface.
5308 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5310 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5313 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5315 if (FreeType
& MEM_RELEASE
)
5318 // ARM3 only supports this VAD in this path
5320 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5323 // Is the caller trying to remove the whole VAD, or remove only a portion
5324 // of it? If no region size is specified, then the assumption is that the
5325 // whole VAD is to be destroyed
5330 // The caller must specify the base address identically to the range
5331 // that is stored in the VAD.
5333 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5335 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5336 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5341 // Now compute the actual start/end addresses based on the VAD
5343 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5344 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5347 // Finally lock the working set and remove the VAD from the VAD tree
5349 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5350 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5351 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5356 // This means the caller wants to release a specific region within
5357 // the range. We have to find out which range this is -- the following
5358 // possibilities exist plus their union (CASE D):
5360 // STARTING ADDRESS ENDING ADDRESS
5361 // [<========][========================================][=========>]
5362 // CASE A CASE B CASE C
5365 // First, check for case A or D
5367 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5372 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5375 // This is the easiest one to handle -- it is identical to
5376 // the code path above when the caller sets a zero region size
5377 // and the whole VAD is destroyed
5379 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5380 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5381 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5386 // This case is pretty easy too -- we compute a bunch of
5387 // pages to decommit, and then push the VAD's starting address
5388 // a bit further down, then decrement the commit charge
5390 // NOT YET IMPLEMENTED IN ARM3.
5392 DPRINT1("Case A not handled\n");
5393 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5397 // After analyzing the VAD, set it to NULL so that we don't
5398 // free it in the exit path
5406 // This is case B or case C. First check for case C
5408 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5410 PMEMORY_AREA MemoryArea
;
5413 // This is pretty easy and similar to case A. We compute the
5414 // amount of pages to decommit, update the VAD's commit charge
5415 // and then change the ending address of the VAD to be a bit
5418 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5419 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5423 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5424 // For ReactOS: shrink the corresponding memory area
5425 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5426 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
5427 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
5428 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
5429 MemoryArea
->EndingAddress
= (PVOID
)(StartingAddress
);
5434 // This is case B and the hardest one. Because we are removing
5435 // a chunk of memory from the very middle of the VAD, we must
5436 // actually split the VAD into two new VADs and compute the
5437 // commit charges for each of them, and reinsert new charges.
5439 // NOT YET IMPLEMENTED IN ARM3.
5441 DPRINT1("Case B not handled\n");
5442 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5447 // After analyzing the VAD, set it to NULL so that we don't
5448 // free it in the exit path
5455 // Now we have a range of pages to dereference, so call the right API
5456 // to do that and then release the working set, since we're done messing
5457 // around with process pages.
5459 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5460 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5461 Status
= STATUS_SUCCESS
;
5465 // Update the process counters
5467 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5468 Process
->CommitCharge
-= CommitReduction
;
5469 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5472 // Unlock the address space and free the VAD in failure cases. Next,
5473 // detach from the target process so we can write the region size and the
5474 // base address to the correct source process, and dereference the target
5477 MmUnlockAddressSpace(AddressSpace
);
5478 if (Vad
) ExFreePool(Vad
);
5479 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5480 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5483 // Use SEH to safely return the region size and the base address of the
5484 // deallocation. If we get an access violation, don't return a failure code
5485 // as the deallocation *has* happened. The caller will just have to figure
5486 // out another way to find out where it is (such as VirtualQuery).
5490 *URegionSize
= PRegionSize
;
5491 *UBaseAddress
= (PVOID
)StartingAddress
;
5493 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5501 // This is the decommit path. You cannot decommit from the following VADs in
5502 // Windows, so fail the vall
5504 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5505 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5506 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5508 DPRINT1("Trying to decommit from invalid VAD\n");
5509 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5514 // If the caller did not specify a region size, first make sure that this
5515 // region is actually committed. If it is, then compute the ending address
5516 // based on the VAD.
5520 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5522 DPRINT1("Decomitting non-committed memory\n");
5523 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5526 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5530 // Decommit the PTEs for the range plus the actual backing pages for the
5531 // range, then reduce that amount from the commit charge in the VAD
5533 CommitReduction
= MiAddressToPte(EndingAddress
) -
5534 MiAddressToPte(StartingAddress
) +
5536 MiDecommitPages((PVOID
)StartingAddress
,
5537 MiAddressToPte(EndingAddress
),
5540 ASSERT(CommitReduction
>= 0);
5541 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5542 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5545 // We are done, go to the exit path without freeing the VAD as it remains
5546 // valid since we have not released the allocation.
5549 Status
= STATUS_SUCCESS
;
5553 // In the failure path, we detach and derefernece the target process, and
5554 // return whatever failure code was sent.
5557 MmUnlockAddressSpace(AddressSpace
);
5558 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5559 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5566 MmGetPhysicalAddress(PVOID Address
)
5568 PHYSICAL_ADDRESS PhysicalAddress
;
5572 /* Check if the PXE/PPE/PDE is valid */
5574 #if (_MI_PAGING_LEVELS == 4)
5575 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5577 #if (_MI_PAGING_LEVELS >= 3)
5578 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5580 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5582 /* Check for large pages */
5583 TempPde
= *MiAddressToPde(Address
);
5584 if (TempPde
.u
.Hard
.LargePage
)
5586 /* Physical address is base page + large page offset */
5587 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5588 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5589 return PhysicalAddress
;
5592 /* Check if the PTE is valid */
5593 TempPte
= *MiAddressToPte(Address
);
5594 if (TempPte
.u
.Hard
.Valid
)
5596 /* Physical address is base page + page offset */
5597 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5598 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5599 return PhysicalAddress
;
5603 KeRosDumpStackFrames(NULL
, 20);
5604 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5605 PhysicalAddress
.QuadPart
= 0;
5606 return PhysicalAddress
;