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 PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 if (TempPte
.u
.Soft
.Transition
)
414 /* Get the PFN entry */
415 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
416 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
418 DPRINT1("Pte %p is transitional!\n", PointerPte
);
420 /* Destroy the PTE */
421 MI_ERASE_PTE(PointerPte
);
423 /* Drop the reference on the page table. */
424 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
426 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
428 /* Make the page free. For prototypes, it will be made free when deleting the section object */
429 if (Pfn1
->u2
.ShareCount
== 0)
431 NT_ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
433 /* And it should be in standby or modified list */
434 ASSERT((Pfn1
->u3
.e1
.PageLocation
== ModifiedPageList
) || (Pfn1
->u3
.e1
.PageLocation
== StandbyPageList
));
436 /* Unlink it and put it back in free list */
437 MiUnlinkPageFromList(Pfn1
);
438 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
440 /* Bring it back into the free list */
441 MiInsertPageInFreeList(PageFrameIndex
);
447 /* Get the PFN entry */
448 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
449 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
451 /* Check if this is a valid, prototype PTE */
452 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
454 /* Get the PDE and make sure it's faulted in */
455 PointerPde
= MiPteToPde(PointerPte
);
456 if (PointerPde
->u
.Hard
.Valid
== 0)
458 #if (_MI_PAGING_LEVELS == 2)
459 /* Could be paged pool access from a new process -- synchronize the page directories */
460 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
463 /* The PDE must be valid at this point */
464 KeBugCheckEx(MEMORY_MANAGEMENT
,
466 (ULONG_PTR
)PointerPte
,
468 (ULONG_PTR
)VirtualAddress
);
470 #if (_MI_PAGING_LEVELS == 2)
473 /* Drop the share count */
474 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
476 /* Either a fork, or this is the shared user data page */
477 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
479 /* If it's not the shared user page, then crash, since there's no fork() yet */
480 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
481 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
483 /* Must be some sort of memory corruption */
484 KeBugCheckEx(MEMORY_MANAGEMENT
,
486 (ULONG_PTR
)PointerPte
,
487 (ULONG_PTR
)PrototypePte
,
488 (ULONG_PTR
)Pfn1
->PteAddress
);
493 MI_ERASE_PTE(PointerPte
);
497 /* Make sure the saved PTE address is valid */
498 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
500 /* The PFN entry is illegal, or invalid */
501 KeBugCheckEx(MEMORY_MANAGEMENT
,
503 (ULONG_PTR
)PointerPte
,
505 (ULONG_PTR
)Pfn1
->PteAddress
);
509 MI_ERASE_PTE(PointerPte
);
511 /* There should only be 1 shared reference count */
512 ASSERT(Pfn1
->u2
.ShareCount
== 1);
514 /* Drop the reference on the page table. */
515 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
517 /* Mark the PFN for deletion and dereference what should be the last ref */
518 MI_SET_PFN_DELETED(Pfn1
);
519 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
521 /* We should eventually do this */
522 //CurrentProcess->NumberOfPrivatePages--;
531 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
532 IN ULONG_PTR EndingAddress
,
535 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
538 PEPROCESS CurrentProcess
;
540 BOOLEAN AddressGap
= FALSE
;
541 PSUBSECTION Subsection
;
543 /* Get out if this is a fake VAD, RosMm will free the marea pages */
544 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
546 /* Grab the process and PTE/PDE for the address being deleted */
547 CurrentProcess
= PsGetCurrentProcess();
548 PointerPde
= MiAddressToPde(Va
);
549 PointerPte
= MiAddressToPte(Va
);
551 /* Check if this is a section VAD or a VM VAD */
552 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
554 /* Don't worry about prototypes */
555 PrototypePte
= LastPrototypePte
= NULL
;
559 /* Get the prototype PTE */
560 PrototypePte
= Vad
->FirstPrototypePte
;
561 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
564 /* In all cases, we don't support fork() yet */
565 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
567 /* Loop the PTE for each VA */
570 /* First keep going until we find a valid PDE */
571 while (!PointerPde
->u
.Long
)
573 /* There are gaps in the address space */
576 /* Still no valid PDE, try the next 4MB (or whatever) */
579 /* Update the PTE on this new boundary */
580 PointerPte
= MiPteToAddress(PointerPde
);
582 /* Check if all the PDEs are invalid, so there's nothing to free */
583 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
584 if (Va
> EndingAddress
) return;
587 /* Now check if the PDE is mapped in */
588 if (!PointerPde
->u
.Hard
.Valid
)
590 /* It isn't, so map it in */
591 PointerPte
= MiPteToAddress(PointerPde
);
592 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
595 /* Now we should have a valid PDE, mapped in, and still have some VA */
596 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
597 ASSERT(Va
<= EndingAddress
);
599 /* Check if this is a section VAD with gaps in it */
600 if ((AddressGap
) && (LastPrototypePte
))
602 /* We need to skip to the next correct prototype PTE */
603 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
605 /* And we need the subsection to skip to the next last prototype PTE */
606 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
610 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
614 /* No more subsections, we are done with prototype PTEs */
619 /* Lock the PFN Database while we delete the PTEs */
620 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
623 /* Capture the PDE and make sure it exists */
624 TempPte
= *PointerPte
;
627 MiDecrementPageTableReferences((PVOID
)Va
);
629 /* Check if the PTE is actually mapped in */
630 if (MI_IS_MAPPED_PTE(&TempPte
))
632 /* Are we dealing with section VAD? */
633 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
635 /* We need to skip to the next correct prototype PTE */
636 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
638 /* And we need the subsection to skip to the next last prototype PTE */
639 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
643 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
647 /* No more subsections, we are done with prototype PTEs */
652 /* Check for prototype PTE */
653 if ((TempPte
.u
.Hard
.Valid
== 0) &&
654 (TempPte
.u
.Soft
.Prototype
== 1))
657 MI_ERASE_PTE(PointerPte
);
661 /* Delete the PTE proper */
662 MiDeletePte(PointerPte
,
670 /* The PTE was never mapped, just nuke it here */
671 MI_ERASE_PTE(PointerPte
);
675 /* Update the address and PTE for it */
680 /* Making sure the PDE is still valid */
681 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
683 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
685 /* The PDE should still be valid at this point */
686 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
688 /* Check remaining PTE count (go back 1 page due to above loop) */
689 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
691 if (PointerPde
->u
.Long
!= 0)
693 /* Delete the PTE proper */
694 MiDeletePte(PointerPde
,
695 MiPteToAddress(PointerPde
),
701 /* Release the lock and get out if we're done */
702 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
703 if (Va
> EndingAddress
) return;
705 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
706 PointerPde
= MiAddressToPde(Va
);
712 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
713 OUT PBOOLEAN HaveBadAddress
,
714 OUT PULONG_PTR BadAddress
)
716 PEXCEPTION_RECORD ExceptionRecord
;
722 *HaveBadAddress
= FALSE
;
725 // Get the exception record
727 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
730 // Look at the exception code
732 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
733 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
734 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
737 // We can tell the address if we have more than one parameter
739 if (ExceptionRecord
->NumberParameters
> 1)
742 // Return the address
744 *HaveBadAddress
= TRUE
;
745 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
750 // Continue executing the next handler
752 return EXCEPTION_EXECUTE_HANDLER
;
757 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
758 IN PVOID SourceAddress
,
759 IN PEPROCESS TargetProcess
,
760 OUT PVOID TargetAddress
,
761 IN SIZE_T BufferSize
,
762 IN KPROCESSOR_MODE PreviousMode
,
763 OUT PSIZE_T ReturnSize
)
765 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
766 PMDL Mdl
= (PMDL
)MdlBuffer
;
767 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
768 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
769 volatile BOOLEAN PagesLocked
;
770 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
771 volatile PVOID MdlAddress
;
773 BOOLEAN HaveBadAddress
;
774 ULONG_PTR BadAddress
;
775 NTSTATUS Status
= STATUS_SUCCESS
;
779 // Calculate the maximum amount of data to move
781 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
782 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
783 CurrentSize
= TotalSize
;
784 RemainingSize
= BufferSize
;
787 // Loop as long as there is still data
789 while (RemainingSize
> 0)
792 // Check if this transfer will finish everything off
794 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
797 // Attach to the source address space
799 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
802 // Reset state for this pass
806 FailedInMoving
= FALSE
;
807 ASSERT(FailedInProbe
== FALSE
);
810 // Protect user-mode copy
815 // If this is our first time, probe the buffer
817 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
820 // Catch a failure here
822 FailedInProbe
= TRUE
;
827 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
832 FailedInProbe
= FALSE
;
836 // Initialize and probe and lock the MDL
838 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
839 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
845 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
854 // Use our SEH handler to pick this up
856 FailedInMapping
= TRUE
;
857 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
861 // Now let go of the source and grab to the target process
863 KeUnstackDetachProcess(&ApcState
);
864 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
867 // Check if this is our first time through
869 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
872 // Catch a failure here
874 FailedInProbe
= TRUE
;
879 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
884 FailedInProbe
= FALSE
;
888 // Now do the actual move
890 FailedInMoving
= TRUE
;
891 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
893 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
898 // Detach from whoever we may be attached to
900 KeUnstackDetachProcess(&ApcState
);
903 // Check if we had mapped the pages
905 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
908 // Check if we had locked the pages
910 if (PagesLocked
) MmUnlockPages(Mdl
);
913 // Check if we hit working set quota
915 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
920 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA
);
924 // Check if we failed during the probe or mapping
926 if ((FailedInProbe
) || (FailedInMapping
))
931 Status
= _SEH2_GetExceptionCode();
932 _SEH2_YIELD(return Status
);
936 // Otherwise, we failed probably during the move
938 *ReturnSize
= BufferSize
- RemainingSize
;
942 // Check if we know exactly where we stopped copying
947 // Return the exact number of bytes copied
949 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
954 // Return partial copy
956 Status
= STATUS_PARTIAL_COPY
;
961 // Check for SEH status
963 if (Status
!= STATUS_SUCCESS
) return Status
;
966 // Detach from target
968 KeUnstackDetachProcess(&ApcState
);
973 MmUnmapLockedPages(MdlAddress
, Mdl
);
977 // Update location and size
979 RemainingSize
-= CurrentSize
;
980 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
981 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
987 *ReturnSize
= BufferSize
;
988 return STATUS_SUCCESS
;
993 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
994 IN PVOID SourceAddress
,
995 IN PEPROCESS TargetProcess
,
996 OUT PVOID TargetAddress
,
997 IN SIZE_T BufferSize
,
998 IN KPROCESSOR_MODE PreviousMode
,
999 OUT PSIZE_T ReturnSize
)
1001 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
1002 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
1003 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
1004 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
1006 KAPC_STATE ApcState
;
1007 BOOLEAN HaveBadAddress
;
1008 ULONG_PTR BadAddress
;
1009 NTSTATUS Status
= STATUS_SUCCESS
;
1013 // Calculate the maximum amount of data to move
1015 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1016 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1017 CurrentSize
= TotalSize
;
1018 RemainingSize
= BufferSize
;
1021 // Check if we can use the stack
1023 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1028 PoolAddress
= (PVOID
)StackBuffer
;
1035 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1036 if (!PoolAddress
) ASSERT(FALSE
);
1037 HavePoolAddress
= TRUE
;
1041 // Loop as long as there is still data
1043 while (RemainingSize
> 0)
1046 // Check if this transfer will finish everything off
1048 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1051 // Attach to the source address space
1053 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1056 // Reset state for this pass
1058 FailedInMoving
= FALSE
;
1059 ASSERT(FailedInProbe
== FALSE
);
1062 // Protect user-mode copy
1067 // If this is our first time, probe the buffer
1069 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1072 // Catch a failure here
1074 FailedInProbe
= TRUE
;
1079 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1084 FailedInProbe
= FALSE
;
1090 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1093 // Now let go of the source and grab to the target process
1095 KeUnstackDetachProcess(&ApcState
);
1096 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1099 // Check if this is our first time through
1101 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1104 // Catch a failure here
1106 FailedInProbe
= TRUE
;
1111 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1116 FailedInProbe
= FALSE
;
1120 // Now do the actual move
1122 FailedInMoving
= TRUE
;
1123 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1125 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1130 // Detach from whoever we may be attached to
1132 KeUnstackDetachProcess(&ApcState
);
1135 // Check if we had allocated pool
1137 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1140 // Check if we failed during the probe
1147 Status
= _SEH2_GetExceptionCode();
1148 _SEH2_YIELD(return Status
);
1152 // Otherwise, we failed, probably during the move
1154 *ReturnSize
= BufferSize
- RemainingSize
;
1158 // Check if we know exactly where we stopped copying
1163 // Return the exact number of bytes copied
1165 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1170 // Return partial copy
1172 Status
= STATUS_PARTIAL_COPY
;
1177 // Check for SEH status
1179 if (Status
!= STATUS_SUCCESS
) return Status
;
1182 // Detach from target
1184 KeUnstackDetachProcess(&ApcState
);
1187 // Update location and size
1189 RemainingSize
-= CurrentSize
;
1190 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1191 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1196 // Check if we had allocated pool
1198 if (HavePoolAddress
) ExFreePoolWithTag(PoolAddress
, 'VmRw');
1203 *ReturnSize
= BufferSize
;
1204 return STATUS_SUCCESS
;
1209 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1210 IN PVOID SourceAddress
,
1211 IN PEPROCESS TargetProcess
,
1212 OUT PVOID TargetAddress
,
1213 IN SIZE_T BufferSize
,
1214 IN KPROCESSOR_MODE PreviousMode
,
1215 OUT PSIZE_T ReturnSize
)
1218 PEPROCESS Process
= SourceProcess
;
1221 // Don't accept zero-sized buffers
1223 if (!BufferSize
) return STATUS_SUCCESS
;
1226 // If we are copying from ourselves, lock the target instead
1228 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1231 // Acquire rundown protection
1233 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1238 return STATUS_PROCESS_IS_TERMINATING
;
1242 // See if we should use the pool copy
1244 if (BufferSize
> MI_POOL_COPY_BYTES
)
1249 Status
= MiDoMappedCopy(SourceProcess
,
1262 Status
= MiDoPoolCopy(SourceProcess
,
1274 ExReleaseRundownProtection(&Process
->RundownProtect
);
1280 MmFlushVirtualMemory(IN PEPROCESS Process
,
1281 IN OUT PVOID
*BaseAddress
,
1282 IN OUT PSIZE_T RegionSize
,
1283 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1291 return STATUS_SUCCESS
;
1296 MiGetPageProtection(IN PMMPTE PointerPte
)
1300 PEPROCESS CurrentProcess
;
1301 PETHREAD CurrentThread
;
1302 BOOLEAN WsSafe
, WsShared
;
1307 /* Copy this PTE's contents */
1308 TempPte
= *PointerPte
;
1310 /* Assure it's not totally zero */
1311 ASSERT(TempPte
.u
.Long
);
1313 /* Check for a special prototype format */
1314 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1315 (TempPte
.u
.Soft
.Prototype
== 1))
1317 /* Check if the prototype PTE is not yet pointing to a PTE */
1318 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1320 /* The prototype PTE contains the protection */
1321 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1324 /* Get a pointer to the underlying shared PTE */
1325 PointerPte
= MiProtoPteToPte(&TempPte
);
1327 /* Since the PTE we want to read can be paged out at any time, we need
1328 to release the working set lock first, so that it can be paged in */
1329 CurrentThread
= PsGetCurrentThread();
1330 CurrentProcess
= PsGetCurrentProcess();
1331 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1336 /* Now read the PTE value */
1337 TempPte
= *PointerPte
;
1339 /* Check if that one is invalid */
1340 if (!TempPte
.u
.Hard
.Valid
)
1342 /* We get the protection directly from this PTE */
1343 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1347 /* The PTE is valid, so we might need to get the protection from
1348 the PFN. Lock the PFN database */
1349 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1351 /* Check if the PDE is still valid */
1352 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1354 /* It's not, make it valid */
1355 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1358 /* Now it's safe to read the PTE value again */
1359 TempPte
= *PointerPte
;
1360 ASSERT(TempPte
.u
.Long
!= 0);
1362 /* Check again if the PTE is invalid */
1363 if (!TempPte
.u
.Hard
.Valid
)
1365 /* The PTE is not valid, so we can use it's protection field */
1366 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1370 /* The PTE is valid, so we can find the protection in the
1371 OriginalPte field of the PFN */
1372 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1373 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1376 /* Release the PFN database */
1377 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1380 /* Lock the working set again */
1381 MiLockProcessWorkingSetForFault(CurrentProcess
,
1389 /* In the easy case of transition or demand zero PTE just return its protection */
1390 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1392 /* If we get here, the PTE is valid, so look up the page in PFN database */
1393 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1394 if (!Pfn
->u3
.e1
.PrototypePte
)
1396 /* Return protection of the original pte */
1397 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1398 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1401 /* This is software PTE */
1402 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1403 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1404 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1405 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1406 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1411 MiQueryAddressState(IN PVOID Va
,
1413 IN PEPROCESS TargetProcess
,
1414 OUT PULONG ReturnedProtect
,
1418 PMMPTE PointerPte
, ProtoPte
;
1420 #if (_MI_PAGING_LEVELS >= 3)
1423 #if (_MI_PAGING_LEVELS >= 4)
1426 MMPTE TempPte
, TempProtoPte
;
1427 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1428 ULONG State
= MEM_RESERVE
, Protect
= 0;
1429 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1430 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1432 /* Only normal VADs supported */
1433 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1435 /* Get the PDE and PTE for the address */
1436 PointerPde
= MiAddressToPde(Va
);
1437 PointerPte
= MiAddressToPte(Va
);
1438 #if (_MI_PAGING_LEVELS >= 3)
1439 PointerPpe
= MiAddressToPpe(Va
);
1441 #if (_MI_PAGING_LEVELS >= 4)
1442 PointerPxe
= MiAddressToPxe(Va
);
1445 /* Return the next range */
1446 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1450 #if (_MI_PAGING_LEVELS >= 4)
1451 /* Does the PXE exist? */
1452 if (PointerPxe
->u
.Long
== 0)
1454 /* It does not, next range starts at the next PXE */
1455 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1459 /* Is the PXE valid? */
1460 if (PointerPxe
->u
.Hard
.Valid
== 0)
1462 /* Is isn't, fault it in (make the PPE accessible) */
1463 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1466 #if (_MI_PAGING_LEVELS >= 3)
1467 /* Does the PPE exist? */
1468 if (PointerPpe
->u
.Long
== 0)
1470 /* It does not, next range starts at the next PPE */
1471 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1475 /* Is the PPE valid? */
1476 if (PointerPpe
->u
.Hard
.Valid
== 0)
1478 /* Is isn't, fault it in (make the PDE accessible) */
1479 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1483 /* Does the PDE exist? */
1484 if (PointerPde
->u
.Long
== 0)
1486 /* It does not, next range starts at the next PDE */
1487 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1491 /* Is the PDE valid? */
1492 if (PointerPde
->u
.Hard
.Valid
== 0)
1494 /* Is isn't, fault it in (make the PTE accessible) */
1495 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1498 /* We have a PTE that we can access now! */
1503 /* Is it safe to try reading the PTE? */
1506 /* FIXME: watch out for large pages */
1507 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1509 /* Capture the PTE */
1510 TempPte
= *PointerPte
;
1511 if (TempPte
.u
.Long
!= 0)
1513 /* The PTE is valid, so it's not zeroed out */
1514 DemandZeroPte
= FALSE
;
1516 /* Is it a decommited, invalid, or faulted PTE? */
1517 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1518 (TempPte
.u
.Hard
.Valid
== 0) &&
1519 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1520 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1522 /* Otherwise our defaults should hold */
1523 ASSERT(Protect
== 0);
1524 ASSERT(State
== MEM_RESERVE
);
1528 /* This means it's committed */
1531 /* We don't support these */
1532 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1533 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1534 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1536 /* Get protection state of this page */
1537 Protect
= MiGetPageProtection(PointerPte
);
1539 /* Check if this is an image-backed VAD */
1540 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1541 (TempPte
.u
.Soft
.Prototype
== 1) &&
1542 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1545 DPRINT1("Not supported\n");
1552 /* Check if this was a demand-zero PTE, since we need to find the state */
1555 /* Not yet handled */
1556 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1557 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1559 /* Check if this is private commited memory, or an section-backed VAD */
1560 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1562 /* Tell caller about the next range */
1563 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1565 /* Get the prototype PTE for this VAD */
1566 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1567 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1570 /* We should unlock the working set, but it's not being held! */
1572 /* Is the prototype PTE actually valid (committed)? */
1573 TempProtoPte
= *ProtoPte
;
1574 if (TempProtoPte
.u
.Long
)
1576 /* Unless this is a memory-mapped file, handle it like private VAD */
1578 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1579 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1582 /* We should re-lock the working set */
1585 else if (Vad
->u
.VadFlags
.MemCommit
)
1587 /* This is committed memory */
1590 /* Convert the protection */
1591 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1595 /* Return the protection code */
1596 *ReturnedProtect
= Protect
;
1602 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1603 IN PVOID BaseAddress
,
1604 OUT PVOID MemoryInformation
,
1605 IN SIZE_T MemoryInformationLength
,
1606 OUT PSIZE_T ReturnLength
)
1608 PEPROCESS TargetProcess
;
1609 NTSTATUS Status
= STATUS_SUCCESS
;
1611 PVOID Address
, NextAddress
;
1612 BOOLEAN Found
= FALSE
;
1613 ULONG NewProtect
, NewState
;
1615 MEMORY_BASIC_INFORMATION MemoryInfo
;
1616 KAPC_STATE ApcState
;
1617 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1618 PMEMORY_AREA MemoryArea
;
1619 SIZE_T ResultLength
;
1621 /* Check for illegal addresses in user-space, or the shared memory area */
1622 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1623 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1625 Address
= PAGE_ALIGN(BaseAddress
);
1627 /* Make up an info structure describing this range */
1628 MemoryInfo
.BaseAddress
= Address
;
1629 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1630 MemoryInfo
.Type
= MEM_PRIVATE
;
1632 /* Special case for shared data */
1633 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1635 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1636 MemoryInfo
.State
= MEM_COMMIT
;
1637 MemoryInfo
.Protect
= PAGE_READONLY
;
1638 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1642 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1643 MemoryInfo
.State
= MEM_RESERVE
;
1644 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1645 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1648 /* Return the data, NtQueryInformation already probed it*/
1649 if (PreviousMode
!= KernelMode
)
1653 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1654 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1658 Status
= _SEH2_GetExceptionCode();
1664 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1665 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1671 /* Check if this is for a local or remote process */
1672 if (ProcessHandle
== NtCurrentProcess())
1674 TargetProcess
= PsGetCurrentProcess();
1678 /* Reference the target process */
1679 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1680 PROCESS_QUERY_INFORMATION
,
1682 ExGetPreviousMode(),
1683 (PVOID
*)&TargetProcess
,
1685 if (!NT_SUCCESS(Status
)) return Status
;
1687 /* Attach to it now */
1688 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1691 /* Lock the address space and make sure the process isn't already dead */
1692 MmLockAddressSpace(&TargetProcess
->Vm
);
1693 if (TargetProcess
->VmDeleted
)
1695 /* Unlock the address space of the process */
1696 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1698 /* Check if we were attached */
1699 if (ProcessHandle
!= NtCurrentProcess())
1701 /* Detach and dereference the process */
1702 KeUnstackDetachProcess(&ApcState
);
1703 ObDereferenceObject(TargetProcess
);
1707 DPRINT1("Process is dying\n");
1708 return STATUS_PROCESS_IS_TERMINATING
;
1712 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1713 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1715 /* Scan on the right */
1716 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1717 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1720 /* Check if this VAD covers the allocation range */
1721 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1722 (BaseVpn
<= Vad
->EndingVpn
))
1729 /* Check if this VAD is too high */
1730 if (BaseVpn
< Vad
->StartingVpn
)
1732 /* Stop if there is no left child */
1733 if (!Vad
->LeftChild
) break;
1735 /* Search on the left next */
1736 Vad
= Vad
->LeftChild
;
1740 /* Then this VAD is too low, keep searching on the right */
1741 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1743 /* Stop if there is no right child */
1744 if (!Vad
->RightChild
) break;
1746 /* Search on the right next */
1747 Vad
= Vad
->RightChild
;
1752 /* Was a VAD found? */
1755 Address
= PAGE_ALIGN(BaseAddress
);
1757 /* Calculate region size */
1760 if (Vad
->StartingVpn
>= BaseVpn
)
1762 /* Region size is the free space till the start of that VAD */
1763 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1767 /* Get the next VAD */
1768 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1771 /* Region size is the free space till the start of that VAD */
1772 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1776 /* Maximum possible region size with that base address */
1777 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1783 /* Maximum possible region size with that base address */
1784 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1787 /* Unlock the address space of the process */
1788 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1790 /* Check if we were attached */
1791 if (ProcessHandle
!= NtCurrentProcess())
1793 /* Detach and derefernece the process */
1794 KeUnstackDetachProcess(&ApcState
);
1795 ObDereferenceObject(TargetProcess
);
1798 /* Build the rest of the initial information block */
1799 MemoryInfo
.BaseAddress
= Address
;
1800 MemoryInfo
.AllocationBase
= NULL
;
1801 MemoryInfo
.AllocationProtect
= 0;
1802 MemoryInfo
.State
= MEM_FREE
;
1803 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1804 MemoryInfo
.Type
= 0;
1806 /* Return the data, NtQueryInformation already probed it*/
1807 if (PreviousMode
!= KernelMode
)
1811 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1812 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1814 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1816 Status
= _SEH2_GetExceptionCode();
1822 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1823 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1829 /* Set the correct memory type based on what kind of VAD this is */
1830 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1831 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1833 MemoryInfo
.Type
= MEM_PRIVATE
;
1835 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1837 MemoryInfo
.Type
= MEM_IMAGE
;
1841 MemoryInfo
.Type
= MEM_MAPPED
;
1844 /* Find the memory area the specified address belongs to */
1845 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1846 ASSERT(MemoryArea
!= NULL
);
1848 /* Determine information dependent on the memory area type */
1849 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1851 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1852 if (!NT_SUCCESS(Status
))
1854 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1855 MemoryArea
, MemoryArea
->StartingAddress
, MemoryArea
->EndingAddress
, BaseAddress
);
1856 NT_ASSERT(NT_SUCCESS(Status
));
1861 /* Build the initial information block */
1862 Address
= PAGE_ALIGN(BaseAddress
);
1863 MemoryInfo
.BaseAddress
= Address
;
1864 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1865 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1866 MemoryInfo
.Type
= MEM_PRIVATE
;
1868 /* Acquire the working set lock (shared is enough) */
1869 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1871 /* Find the largest chunk of memory which has the same state and protection mask */
1872 MemoryInfo
.State
= MiQueryAddressState(Address
,
1875 &MemoryInfo
.Protect
,
1877 Address
= NextAddress
;
1878 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1880 /* Keep going unless the state or protection mask changed */
1881 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1882 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1883 Address
= NextAddress
;
1886 /* Release the working set lock */
1887 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1889 /* Check if we went outside of the VAD */
1890 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1892 /* Set the end of the VAD as the end address */
1893 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1896 /* Now that we know the last VA address, calculate the region size */
1897 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1900 /* Unlock the address space of the process */
1901 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1903 /* Check if we were attached */
1904 if (ProcessHandle
!= NtCurrentProcess())
1906 /* Detach and derefernece the process */
1907 KeUnstackDetachProcess(&ApcState
);
1908 ObDereferenceObject(TargetProcess
);
1911 /* Return the data, NtQueryInformation already probed it */
1912 if (PreviousMode
!= KernelMode
)
1916 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1917 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1919 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1921 Status
= _SEH2_GetExceptionCode();
1927 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1928 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1932 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1933 "State: %lx Type: %lx Size: %lx\n",
1934 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1935 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1936 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1943 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1944 IN ULONG_PTR EndingAddress
,
1946 IN PEPROCESS Process
)
1948 PMMPTE PointerPte
, LastPte
, PointerPde
;
1949 BOOLEAN OnBoundary
= TRUE
;
1952 /* Get the PDE and PTE addresses */
1953 PointerPde
= MiAddressToPde(StartingAddress
);
1954 PointerPte
= MiAddressToPte(StartingAddress
);
1955 LastPte
= MiAddressToPte(EndingAddress
);
1957 /* Loop all the PTEs */
1958 while (PointerPte
<= LastPte
)
1960 /* Check if we've hit an new PDE boundary */
1963 /* Is this PDE demand zero? */
1964 PointerPde
= MiAddressToPte(PointerPte
);
1965 if (PointerPde
->u
.Long
!= 0)
1967 /* It isn't -- is it valid? */
1968 if (PointerPde
->u
.Hard
.Valid
== 0)
1970 /* Nope, fault it in */
1971 PointerPte
= MiPteToAddress(PointerPde
);
1972 MiMakeSystemAddressValid(PointerPte
, Process
);
1977 /* The PTE was already valid, so move to the next one */
1979 PointerPte
= MiPteToAddress(PointerPde
);
1981 /* Is the entire VAD committed? If not, fail */
1982 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1984 /* Everything is committed so far past the range, return true */
1985 if (PointerPte
> LastPte
) return TRUE
;
1989 /* Is the PTE demand zero? */
1990 if (PointerPte
->u
.Long
== 0)
1992 /* Is the entire VAD committed? If not, fail */
1993 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1997 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1998 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1999 (PointerPte
->u
.Hard
.Valid
== 0) &&
2000 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
2001 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
2003 /* Then part of the range is decommitted, so fail */
2008 /* Move to the next PTE */
2010 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2013 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2019 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2020 IN OUT PVOID
*BaseAddress
,
2021 IN OUT PSIZE_T NumberOfBytesToProtect
,
2022 IN ULONG NewAccessProtection
,
2023 OUT PULONG OldAccessProtection OPTIONAL
)
2025 PMEMORY_AREA MemoryArea
;
2026 PMMSUPPORT AddressSpace
;
2027 ULONG OldAccessProtection_
;
2030 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2031 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2033 AddressSpace
= &Process
->Vm
;
2034 MmLockAddressSpace(AddressSpace
);
2035 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2036 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2038 MmUnlockAddressSpace(AddressSpace
);
2039 return STATUS_UNSUCCESSFUL
;
2042 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2044 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2045 Status
= MmProtectSectionView(AddressSpace
,
2048 *NumberOfBytesToProtect
,
2049 NewAccessProtection
,
2050 OldAccessProtection
);
2052 MmUnlockAddressSpace(AddressSpace
);
2059 MiProtectVirtualMemory(IN PEPROCESS Process
,
2060 IN OUT PVOID
*BaseAddress
,
2061 IN OUT PSIZE_T NumberOfBytesToProtect
,
2062 IN ULONG NewAccessProtection
,
2063 OUT PULONG OldAccessProtection OPTIONAL
)
2065 PMEMORY_AREA MemoryArea
;
2067 PMMSUPPORT AddressSpace
;
2068 ULONG_PTR StartingAddress
, EndingAddress
;
2069 PMMPTE PointerPde
, PointerPte
, LastPte
;
2072 ULONG ProtectionMask
, OldProtect
;
2074 NTSTATUS Status
= STATUS_SUCCESS
;
2075 PETHREAD Thread
= PsGetCurrentThread();
2076 TABLE_SEARCH_RESULT Result
;
2078 /* Calculate base address for the VAD */
2079 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2080 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2082 /* Calculate the protection mask and make sure it's valid */
2083 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2084 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2086 DPRINT1("Invalid protection mask\n");
2087 return STATUS_INVALID_PAGE_PROTECTION
;
2090 /* Check for ROS specific memory area */
2091 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2092 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2095 return MiRosProtectVirtualMemory(Process
,
2097 NumberOfBytesToProtect
,
2098 NewAccessProtection
,
2099 OldAccessProtection
);
2102 /* Lock the address space and make sure the process isn't already dead */
2103 AddressSpace
= MmGetCurrentAddressSpace();
2104 MmLockAddressSpace(AddressSpace
);
2105 if (Process
->VmDeleted
)
2107 DPRINT1("Process is dying\n");
2108 Status
= STATUS_PROCESS_IS_TERMINATING
;
2112 /* Get the VAD for this address range, and make sure it exists */
2113 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2114 EndingAddress
>> PAGE_SHIFT
,
2116 (PMMADDRESS_NODE
*)&Vad
);
2117 if (Result
!= TableFoundNode
)
2119 DPRINT("Could not find a VAD for this allocation\n");
2120 Status
= STATUS_CONFLICTING_ADDRESSES
;
2124 /* Make sure the address is within this VAD's boundaries */
2125 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2126 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2128 Status
= STATUS_CONFLICTING_ADDRESSES
;
2132 /* These kinds of VADs are not supported atm */
2133 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2134 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2135 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2137 DPRINT1("Illegal VAD for attempting to set protection\n");
2138 Status
= STATUS_CONFLICTING_ADDRESSES
;
2142 /* Check for a VAD whose protection can't be changed */
2143 if (Vad
->u
.VadFlags
.NoChange
== 1)
2145 DPRINT1("Trying to change protection of a NoChange VAD\n");
2146 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2150 /* Is this section, or private memory? */
2151 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2153 /* Not yet supported */
2154 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2156 DPRINT1("Illegal VAD for attempting to set protection\n");
2157 Status
= STATUS_CONFLICTING_ADDRESSES
;
2161 /* Rotate VADs are not yet supported */
2162 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2164 DPRINT1("Illegal VAD for attempting to set protection\n");
2165 Status
= STATUS_CONFLICTING_ADDRESSES
;
2169 /* Not valid on section files */
2170 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2173 DPRINT1("Invalid protection flags for section\n");
2174 Status
= STATUS_INVALID_PARAMETER_4
;
2178 /* Check if data or page file mapping protection PTE is compatible */
2179 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2182 DPRINT1("Fixme: Not checking for valid protection\n");
2185 /* This is a section, and this is not yet supported */
2186 DPRINT1("Section protection not yet supported\n");
2191 /* Private memory, check protection flags */
2192 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2193 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2195 DPRINT1("Invalid protection flags for private memory\n");
2196 Status
= STATUS_INVALID_PARAMETER_4
;
2200 /* Lock the working set */
2201 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2203 /* Check if all pages in this range are committed */
2204 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2211 DPRINT1("The entire range is not committed\n");
2212 Status
= STATUS_NOT_COMMITTED
;
2213 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2217 /* Compute starting and ending PTE and PDE addresses */
2218 PointerPde
= MiAddressToPde(StartingAddress
);
2219 PointerPte
= MiAddressToPte(StartingAddress
);
2220 LastPte
= MiAddressToPte(EndingAddress
);
2222 /* Make this PDE valid */
2223 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2225 /* Save protection of the first page */
2226 if (PointerPte
->u
.Long
!= 0)
2228 /* Capture the page protection and make the PDE valid */
2229 OldProtect
= MiGetPageProtection(PointerPte
);
2230 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2234 /* Grab the old protection from the VAD itself */
2235 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2238 /* Loop all the PTEs now */
2239 while (PointerPte
<= LastPte
)
2241 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2242 if (MiIsPteOnPdeBoundary(PointerPte
))
2244 PointerPde
= MiAddressToPte(PointerPte
);
2245 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2248 /* Capture the PTE and check if it was empty */
2249 PteContents
= *PointerPte
;
2250 if (PteContents
.u
.Long
== 0)
2252 /* This used to be a zero PTE and it no longer is, so we must add a
2253 reference to the pagetable. */
2254 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2257 /* Check what kind of PTE we are dealing with */
2258 if (PteContents
.u
.Hard
.Valid
== 1)
2260 /* Get the PFN entry */
2261 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2263 /* We don't support this yet */
2264 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2266 /* Check if the page should not be accessible at all */
2267 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2268 (NewAccessProtection
& PAGE_GUARD
))
2270 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2272 /* Mark the PTE as transition and change its protection */
2273 PteContents
.u
.Hard
.Valid
= 0;
2274 PteContents
.u
.Soft
.Transition
= 1;
2275 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2276 /* Decrease PFN share count and write the PTE */
2277 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2278 // FIXME: remove the page from the WS
2279 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2281 // FIXME: Should invalidate entry in every CPU TLB
2284 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2286 /* We are done for this PTE */
2287 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2291 /* Write the protection mask and write it with a TLB flush */
2292 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2293 MiFlushTbAndCapture(Vad
,
2302 /* We don't support these cases yet */
2303 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2304 //ASSERT(PteContents.u.Soft.Transition == 0);
2306 /* The PTE is already demand-zero, just update the protection mask */
2307 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2308 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2309 ASSERT(PointerPte
->u
.Long
!= 0);
2312 /* Move to the next PTE */
2316 /* Unlock the working set */
2317 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2320 /* Unlock the address space */
2321 MmUnlockAddressSpace(AddressSpace
);
2323 /* Return parameters and success */
2324 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2325 *BaseAddress
= (PVOID
)StartingAddress
;
2326 *OldAccessProtection
= OldProtect
;
2327 return STATUS_SUCCESS
;
2330 /* Unlock the address space and return the failure code */
2331 MmUnlockAddressSpace(AddressSpace
);
2337 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2338 IN PEPROCESS TargetProcess
,
2341 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2344 // Sanity checks. The latter is because we only use this function with the
2345 // PFN lock not held, so it may go away in the future.
2347 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2348 ASSERT(OldIrql
== MM_NOIRQL
);
2351 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2352 // return the same address as the PDE on 2-level page table systems.
2354 // If everything is already valid, there is nothing to do.
2356 PointerPpe
= MiAddressToPte(PointerPde
);
2357 PointerPxe
= MiAddressToPde(PointerPde
);
2358 if ((PointerPxe
->u
.Hard
.Valid
) &&
2359 (PointerPpe
->u
.Hard
.Valid
) &&
2360 (PointerPde
->u
.Hard
.Valid
))
2366 // At least something is invalid, so begin by getting the PTE for the PDE itself
2367 // and then lookup each additional level. We must do it in this precise order
2368 // because the pagfault.c code (as well as in Windows) depends that the next
2369 // level up (higher) must be valid when faulting a lower level
2371 PointerPte
= MiPteToAddress(PointerPde
);
2375 // Make sure APCs continued to be disabled
2377 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2380 // First, make the PXE valid if needed
2382 if (!PointerPxe
->u
.Hard
.Valid
)
2384 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2385 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2391 if (!PointerPpe
->u
.Hard
.Valid
)
2393 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2394 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2398 // And finally, make the PDE itself valid.
2400 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2403 // This should've worked the first time so the loop is really just for
2404 // show -- ASSERT that we're actually NOT going to be looping.
2406 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2407 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2408 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2409 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2410 !(PointerPpe
->u
.Hard
.Valid
) ||
2411 !(PointerPde
->u
.Hard
.Valid
));
2416 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2422 PFN_NUMBER PageFrameIndex
;
2426 // Acquire the PFN lock and loop all the PTEs in the list
2428 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2429 for (i
= 0; i
!= Count
; i
++)
2432 // The PTE must currently be valid
2434 TempPte
= *ValidPteList
[i
];
2435 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2438 // Get the PFN entry for the page itself, and then for its page table
2440 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2441 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2442 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2445 // Decrement the share count on the page table, and then on the page
2448 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2449 MI_SET_PFN_DELETED(Pfn1
);
2450 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2453 // Make the page decommitted
2455 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2459 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2460 // and then release the PFN lock
2463 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2468 MiDecommitPages(IN PVOID StartingAddress
,
2469 IN PMMPTE EndingPte
,
2470 IN PEPROCESS Process
,
2473 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2474 ULONG CommitReduction
= 0;
2475 PMMPTE ValidPteList
[256];
2479 PETHREAD CurrentThread
= PsGetCurrentThread();
2482 // Get the PTE and PTE for the address, and lock the working set
2483 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2484 // commited range ends so that we can do the right accounting.
2486 PointerPde
= MiAddressToPde(StartingAddress
);
2487 PointerPte
= MiAddressToPte(StartingAddress
);
2488 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2489 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2492 // Make the PDE valid, and now loop through each page's worth of data
2494 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2495 while (PointerPte
<= EndingPte
)
2498 // Check if we've crossed a PDE boundary
2500 if (MiIsPteOnPdeBoundary(PointerPte
))
2503 // Get the new PDE and flush the valid PTEs we had built up until
2504 // now. This helps reduce the amount of TLB flushing we have to do.
2505 // Note that Windows does a much better job using timestamps and
2506 // such, and does not flush the entire TLB all the time, but right
2507 // now we have bigger problems to worry about than TLB flushing.
2509 PointerPde
= MiAddressToPde(StartingAddress
);
2512 MiProcessValidPteList(ValidPteList
, PteCount
);
2517 // Make this PDE valid
2519 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2523 // Read this PTE. It might be active or still demand-zero.
2525 PteContents
= *PointerPte
;
2526 if (PteContents
.u
.Long
)
2529 // The PTE is active. It might be valid and in a working set, or
2530 // it might be a prototype PTE or paged out or even in transition.
2532 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2535 // It's already decommited, so there's nothing for us to do here
2542 // Remove it from the counters, and check if it was valid or not
2544 //Process->NumberOfPrivatePages--;
2545 if (PteContents
.u
.Hard
.Valid
)
2548 // It's valid. At this point make sure that it is not a ROS
2549 // PFN. Also, we don't support ProtoPTEs in this code path.
2551 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2552 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2553 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2556 // Flush any pending PTEs that we had not yet flushed, if our
2557 // list has gotten too big, then add this PTE to the flush list.
2559 if (PteCount
== 256)
2561 MiProcessValidPteList(ValidPteList
, PteCount
);
2564 ValidPteList
[PteCount
++] = PointerPte
;
2569 // We do not support any of these other scenarios at the moment
2571 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2572 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2573 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2576 // So the only other possibility is that it is still a demand
2577 // zero PTE, in which case we undo the accounting we did
2578 // earlier and simply make the page decommitted.
2580 //Process->NumberOfPrivatePages++;
2581 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2588 // This used to be a zero PTE and it no longer is, so we must add a
2589 // reference to the pagetable.
2591 MiIncrementPageTableReferences(StartingAddress
);
2594 // Next, we account for decommitted PTEs and make the PTE as such
2596 if (PointerPte
> CommitPte
) CommitReduction
++;
2597 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2601 // Move to the next PTE and the next address
2604 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2608 // Flush any dangling PTEs from the loop in the last page table, and then
2609 // release the working set and return the commit reduction accounting.
2611 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2612 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2613 return CommitReduction
;
2616 /* PUBLIC FUNCTIONS ***********************************************************/
2623 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2634 MmSecureVirtualMemory(IN PVOID Address
,
2638 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2647 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2649 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2652 /* SYSTEM CALLS ***************************************************************/
2656 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2657 IN PVOID BaseAddress
,
2659 IN SIZE_T NumberOfBytesToRead
,
2660 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2662 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2664 NTSTATUS Status
= STATUS_SUCCESS
;
2665 SIZE_T BytesRead
= 0;
2669 // Check if we came from user mode
2671 if (PreviousMode
!= KernelMode
)
2674 // Validate the read addresses
2676 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2677 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2678 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2679 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2682 // Don't allow to write into kernel space
2684 return STATUS_ACCESS_VIOLATION
;
2688 // Enter SEH for probe
2693 // Probe the output value
2695 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2697 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2700 // Get exception code
2702 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2708 // Don't do zero-byte transfers
2710 if (NumberOfBytesToRead
)
2713 // Reference the process
2715 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2721 if (NT_SUCCESS(Status
))
2726 Status
= MmCopyVirtualMemory(Process
,
2728 PsGetCurrentProcess(),
2730 NumberOfBytesToRead
,
2735 // Dereference the process
2737 ObDereferenceObject(Process
);
2742 // Check if the caller sent this parameter
2744 if (NumberOfBytesRead
)
2747 // Enter SEH to guard write
2752 // Return the number of bytes read
2754 *NumberOfBytesRead
= BytesRead
;
2756 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2770 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2771 IN PVOID BaseAddress
,
2773 IN SIZE_T NumberOfBytesToWrite
,
2774 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2776 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2778 NTSTATUS Status
= STATUS_SUCCESS
;
2779 SIZE_T BytesWritten
= 0;
2783 // Check if we came from user mode
2785 if (PreviousMode
!= KernelMode
)
2788 // Validate the read addresses
2790 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2791 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2792 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2793 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2796 // Don't allow to write into kernel space
2798 return STATUS_ACCESS_VIOLATION
;
2802 // Enter SEH for probe
2807 // Probe the output value
2809 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2811 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2814 // Get exception code
2816 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2822 // Don't do zero-byte transfers
2824 if (NumberOfBytesToWrite
)
2827 // Reference the process
2829 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2835 if (NT_SUCCESS(Status
))
2840 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2844 NumberOfBytesToWrite
,
2849 // Dereference the process
2851 ObDereferenceObject(Process
);
2856 // Check if the caller sent this parameter
2858 if (NumberOfBytesWritten
)
2861 // Enter SEH to guard write
2866 // Return the number of bytes written
2868 *NumberOfBytesWritten
= BytesWritten
;
2870 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2884 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2885 IN OUT PVOID
*UnsafeBaseAddress
,
2886 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2887 IN ULONG NewAccessProtection
,
2888 OUT PULONG UnsafeOldAccessProtection
)
2891 ULONG OldAccessProtection
;
2893 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2894 PVOID BaseAddress
= NULL
;
2895 SIZE_T NumberOfBytesToProtect
= 0;
2896 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2898 BOOLEAN Attached
= FALSE
;
2899 KAPC_STATE ApcState
;
2903 // Check for valid protection flags
2905 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2906 if (Protection
!= PAGE_NOACCESS
&&
2907 Protection
!= PAGE_READONLY
&&
2908 Protection
!= PAGE_READWRITE
&&
2909 Protection
!= PAGE_WRITECOPY
&&
2910 Protection
!= PAGE_EXECUTE
&&
2911 Protection
!= PAGE_EXECUTE_READ
&&
2912 Protection
!= PAGE_EXECUTE_READWRITE
&&
2913 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2918 return STATUS_INVALID_PAGE_PROTECTION
;
2922 // Check if we came from user mode
2924 if (PreviousMode
!= KernelMode
)
2927 // Enter SEH for probing
2932 // Validate all outputs
2934 ProbeForWritePointer(UnsafeBaseAddress
);
2935 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2936 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2941 BaseAddress
= *UnsafeBaseAddress
;
2942 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2944 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2947 // Get exception code
2949 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2958 BaseAddress
= *UnsafeBaseAddress
;
2959 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2963 // Catch illegal base address
2965 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2968 // Catch illegal region size
2970 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2975 return STATUS_INVALID_PARAMETER_3
;
2979 // 0 is also illegal
2981 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2984 // Get a reference to the process
2986 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2987 PROCESS_VM_OPERATION
,
2992 if (!NT_SUCCESS(Status
)) return Status
;
2995 // Check if we should attach
2997 if (CurrentProcess
!= Process
)
3002 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3007 // Do the actual work
3009 Status
= MiProtectVirtualMemory(Process
,
3011 &NumberOfBytesToProtect
,
3012 NewAccessProtection
,
3013 &OldAccessProtection
);
3018 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3021 // Release reference
3023 ObDereferenceObject(Process
);
3026 // Enter SEH to return data
3031 // Return data to user
3033 *UnsafeOldAccessProtection
= OldAccessProtection
;
3034 *UnsafeBaseAddress
= BaseAddress
;
3035 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3037 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3054 // HACK until we have proper WSLIST support
3055 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3057 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3059 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3071 // HACK until we have proper WSLIST support
3072 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3074 if (!Wsle
->u1
.e1
.LockedInWs
&&
3075 !Wsle
->u1
.e1
.LockedInMemory
)
3077 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3080 if (LockType
& MAP_PROCESS
)
3081 Wsle
->u1
.e1
.LockedInWs
= 1;
3082 if (LockType
& MAP_SYSTEM
)
3083 Wsle
->u1
.e1
.LockedInMemory
= 1;
3092 // HACK until we have proper WSLIST support
3093 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3095 if (LockType
& MAP_PROCESS
)
3096 Wsle
->u1
.e1
.LockedInWs
= 0;
3097 if (LockType
& MAP_SYSTEM
)
3098 Wsle
->u1
.e1
.LockedInMemory
= 0;
3100 if (!Wsle
->u1
.e1
.LockedInWs
&&
3101 !Wsle
->u1
.e1
.LockedInMemory
)
3103 MiDereferencePfnAndDropLockCount(Pfn1
);
3109 MiCheckVadsForLockOperation(
3110 _Inout_ PVOID
*BaseAddress
,
3111 _Inout_ PSIZE_T RegionSize
,
3112 _Inout_ PVOID
*EndAddress
)
3118 /* Get the base address and align the start address */
3119 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3120 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3121 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3123 /* First loop and check all VADs */
3124 CurrentVa
= *BaseAddress
;
3125 while (CurrentVa
< *EndAddress
)
3128 Vad
= MiLocateAddress(CurrentVa
);
3131 /// FIXME: this might be a memory area for a section view...
3132 return STATUS_ACCESS_VIOLATION
;
3135 /* Check VAD type */
3136 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3137 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3138 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3140 *EndAddress
= CurrentVa
;
3141 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3142 return STATUS_INCOMPATIBLE_FILE_MAP
;
3145 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3148 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3149 return STATUS_SUCCESS
;
3154 MiLockVirtualMemory(
3155 IN OUT PVOID
*BaseAddress
,
3156 IN OUT PSIZE_T RegionSize
,
3159 PEPROCESS CurrentProcess
;
3160 PMMSUPPORT AddressSpace
;
3161 PVOID CurrentVa
, EndAddress
;
3162 PMMPTE PointerPte
, LastPte
;
3164 #if (_MI_PAGING_LEVELS >= 3)
3167 #if (_MI_PAGING_LEVELS == 4)
3171 NTSTATUS Status
, TempStatus
;
3173 /* Lock the address space */
3174 AddressSpace
= MmGetCurrentAddressSpace();
3175 MmLockAddressSpace(AddressSpace
);
3177 /* Make sure we still have an address space */
3178 CurrentProcess
= PsGetCurrentProcess();
3179 if (CurrentProcess
->VmDeleted
)
3181 Status
= STATUS_PROCESS_IS_TERMINATING
;
3185 /* Check the VADs in the requested range */
3186 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3187 if (!NT_SUCCESS(Status
))
3192 /* Enter SEH for probing */
3195 /* Loop all pages and probe them */
3196 CurrentVa
= *BaseAddress
;
3197 while (CurrentVa
< EndAddress
)
3199 (void)(*(volatile CHAR
*)CurrentVa
);
3200 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3203 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3205 Status
= _SEH2_GetExceptionCode();
3210 /* All pages were accessible, since we hold the address space lock, nothing
3211 can be de-committed. Assume success for now. */
3212 Status
= STATUS_SUCCESS
;
3214 /* Get the PTE and PDE */
3215 PointerPte
= MiAddressToPte(*BaseAddress
);
3216 PointerPde
= MiAddressToPde(*BaseAddress
);
3217 #if (_MI_PAGING_LEVELS >= 3)
3218 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3220 #if (_MI_PAGING_LEVELS == 4)
3221 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3224 /* Get the last PTE */
3225 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3227 /* Lock the process working set */
3228 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3230 /* Loop the pages */
3233 /* Check for a page that is not accessible */
3235 #if (_MI_PAGING_LEVELS == 4)
3236 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3238 #if (_MI_PAGING_LEVELS >= 3)
3239 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3241 (PointerPde
->u
.Hard
.Valid
== 0) ||
3242 (PointerPte
->u
.Hard
.Valid
== 0))
3244 /* Release process working set */
3245 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3247 /* Access the page */
3248 CurrentVa
= MiPteToAddress(PointerPte
);
3250 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3251 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3252 if (!NT_SUCCESS(TempStatus
))
3254 // This should only happen, when remote backing storage is not accessible
3256 Status
= TempStatus
;
3260 /* Lock the process working set */
3261 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3265 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3266 ASSERT(Pfn1
!= NULL
);
3268 /* Check the previous lock status */
3269 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3271 Status
= STATUS_WAS_LOCKED
;
3275 MI_LOCK_VA(Pfn1
, MapType
);
3277 /* Go to the next PTE */
3280 /* Check if we're on a PDE boundary */
3281 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3282 #if (_MI_PAGING_LEVELS >= 3)
3283 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3285 #if (_MI_PAGING_LEVELS == 4)
3286 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3288 } while (PointerPte
<= LastPte
);
3290 /* Release process working set */
3291 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3294 /* Unlock address space */
3295 MmUnlockAddressSpace(AddressSpace
);
3302 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3303 IN OUT PVOID
*BaseAddress
,
3304 IN OUT PSIZE_T NumberOfBytesToLock
,
3308 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3310 BOOLEAN Attached
= FALSE
;
3311 KAPC_STATE ApcState
;
3312 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3313 PVOID CapturedBaseAddress
;
3314 SIZE_T CapturedBytesToLock
;
3320 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3323 // Invalid set of flags
3325 return STATUS_INVALID_PARAMETER
;
3329 // At least one flag must be specified
3331 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3336 return STATUS_INVALID_PARAMETER
;
3340 // Enter SEH for probing
3345 // Validate output data
3347 ProbeForWritePointer(BaseAddress
);
3348 ProbeForWriteSize_t(NumberOfBytesToLock
);
3353 CapturedBaseAddress
= *BaseAddress
;
3354 CapturedBytesToLock
= *NumberOfBytesToLock
;
3356 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3359 // Get exception code
3361 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3366 // Catch illegal base address
3368 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3371 // Catch illegal region size
3373 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3378 return STATUS_INVALID_PARAMETER
;
3382 // 0 is also illegal
3384 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3387 // Get a reference to the process
3389 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3390 PROCESS_VM_OPERATION
,
3395 if (!NT_SUCCESS(Status
)) return Status
;
3398 // Check if this is is system-mapped
3400 if (MapType
& MAP_SYSTEM
)
3403 // Check for required privilege
3405 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3408 // Fail: Don't have it
3410 ObDereferenceObject(Process
);
3411 return STATUS_PRIVILEGE_NOT_HELD
;
3416 // Check if we should attach
3418 if (CurrentProcess
!= Process
)
3423 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3428 // Call the internal function
3430 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3431 &CapturedBytesToLock
,
3437 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3440 // Release reference
3442 ObDereferenceObject(Process
);
3445 // Enter SEH to return data
3450 // Return data to user
3452 *BaseAddress
= CapturedBaseAddress
;
3453 *NumberOfBytesToLock
= CapturedBytesToLock
;
3455 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3458 // Get exception code
3460 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3473 MiUnlockVirtualMemory(
3474 IN OUT PVOID
*BaseAddress
,
3475 IN OUT PSIZE_T RegionSize
,
3478 PEPROCESS CurrentProcess
;
3479 PMMSUPPORT AddressSpace
;
3481 PMMPTE PointerPte
, LastPte
;
3483 #if (_MI_PAGING_LEVELS >= 3)
3486 #if (_MI_PAGING_LEVELS == 4)
3492 /* Lock the address space */
3493 AddressSpace
= MmGetCurrentAddressSpace();
3494 MmLockAddressSpace(AddressSpace
);
3496 /* Make sure we still have an address space */
3497 CurrentProcess
= PsGetCurrentProcess();
3498 if (CurrentProcess
->VmDeleted
)
3500 Status
= STATUS_PROCESS_IS_TERMINATING
;
3504 /* Check the VADs in the requested range */
3505 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3507 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3508 incompatible VAD we continue, like Windows does */
3509 if (Status
== STATUS_ACCESS_VIOLATION
)
3511 Status
= STATUS_NOT_LOCKED
;
3515 /* Get the PTE and PDE */
3516 PointerPte
= MiAddressToPte(*BaseAddress
);
3517 PointerPde
= MiAddressToPde(*BaseAddress
);
3518 #if (_MI_PAGING_LEVELS >= 3)
3519 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3521 #if (_MI_PAGING_LEVELS == 4)
3522 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3525 /* Get the last PTE */
3526 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3528 /* Lock the process working set */
3529 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3531 /* Loop the pages */
3534 /* Check for a page that is not present */
3536 #if (_MI_PAGING_LEVELS == 4)
3537 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3539 #if (_MI_PAGING_LEVELS >= 3)
3540 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3542 (PointerPde
->u
.Hard
.Valid
== 0) ||
3543 (PointerPte
->u
.Hard
.Valid
== 0))
3545 /* Remember it, but keep going */
3546 Status
= STATUS_NOT_LOCKED
;
3551 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3552 ASSERT(Pfn1
!= NULL
);
3554 /* Check if all of the requested locks are present */
3555 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3556 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3558 /* Remember it, but keep going */
3559 Status
= STATUS_NOT_LOCKED
;
3561 /* Check if no lock is present */
3562 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3564 DPRINT1("FIXME: Should remove the page from WS\n");
3569 /* Go to the next PTE */
3572 /* Check if we're on a PDE boundary */
3573 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3574 #if (_MI_PAGING_LEVELS >= 3)
3575 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3577 #if (_MI_PAGING_LEVELS == 4)
3578 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3580 } while (PointerPte
<= LastPte
);
3582 /* Check if we hit a page that was not locked */
3583 if (Status
== STATUS_NOT_LOCKED
)
3585 goto CleanupWithWsLock
;
3588 /* All pages in the region were locked, so unlock them all */
3590 /* Get the PTE and PDE */
3591 PointerPte
= MiAddressToPte(*BaseAddress
);
3592 PointerPde
= MiAddressToPde(*BaseAddress
);
3593 #if (_MI_PAGING_LEVELS >= 3)
3594 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3596 #if (_MI_PAGING_LEVELS == 4)
3597 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3600 /* Loop the pages */
3604 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3605 MI_UNLOCK_VA(Pfn1
, MapType
);
3607 /* Go to the next PTE */
3610 /* Check if we're on a PDE boundary */
3611 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3612 #if (_MI_PAGING_LEVELS >= 3)
3613 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3615 #if (_MI_PAGING_LEVELS == 4)
3616 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3618 } while (PointerPte
<= LastPte
);
3620 /* Everything is done */
3621 Status
= STATUS_SUCCESS
;
3625 /* Release process working set */
3626 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3629 /* Unlock address space */
3630 MmUnlockAddressSpace(AddressSpace
);
3638 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3639 IN OUT PVOID
*BaseAddress
,
3640 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3644 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3646 BOOLEAN Attached
= FALSE
;
3647 KAPC_STATE ApcState
;
3648 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3649 PVOID CapturedBaseAddress
;
3650 SIZE_T CapturedBytesToUnlock
;
3656 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3659 // Invalid set of flags
3661 return STATUS_INVALID_PARAMETER
;
3665 // At least one flag must be specified
3667 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3672 return STATUS_INVALID_PARAMETER
;
3676 // Enter SEH for probing
3681 // Validate output data
3683 ProbeForWritePointer(BaseAddress
);
3684 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3689 CapturedBaseAddress
= *BaseAddress
;
3690 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3692 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3695 // Get exception code
3697 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3702 // Catch illegal base address
3704 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3707 // Catch illegal region size
3709 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3714 return STATUS_INVALID_PARAMETER
;
3718 // 0 is also illegal
3720 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3723 // Get a reference to the process
3725 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3726 PROCESS_VM_OPERATION
,
3731 if (!NT_SUCCESS(Status
)) return Status
;
3734 // Check if this is is system-mapped
3736 if (MapType
& MAP_SYSTEM
)
3739 // Check for required privilege
3741 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3744 // Fail: Don't have it
3746 ObDereferenceObject(Process
);
3747 return STATUS_PRIVILEGE_NOT_HELD
;
3752 // Check if we should attach
3754 if (CurrentProcess
!= Process
)
3759 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3764 // Call the internal function
3766 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3767 &CapturedBytesToUnlock
,
3773 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3776 // Release reference
3778 ObDereferenceObject(Process
);
3781 // Enter SEH to return data
3786 // Return data to user
3788 *BaseAddress
= CapturedBaseAddress
;
3789 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3791 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3794 // Get exception code
3796 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3803 return STATUS_SUCCESS
;
3808 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3809 IN OUT PVOID
*BaseAddress
,
3810 IN OUT PSIZE_T NumberOfBytesToFlush
,
3811 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3815 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3816 PVOID CapturedBaseAddress
;
3817 SIZE_T CapturedBytesToFlush
;
3818 IO_STATUS_BLOCK LocalStatusBlock
;
3822 // Check if we came from user mode
3824 if (PreviousMode
!= KernelMode
)
3827 // Enter SEH for probing
3832 // Validate all outputs
3834 ProbeForWritePointer(BaseAddress
);
3835 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3836 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3841 CapturedBaseAddress
= *BaseAddress
;
3842 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3844 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3847 // Get exception code
3849 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3858 CapturedBaseAddress
= *BaseAddress
;
3859 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3863 // Catch illegal base address
3865 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3868 // Catch illegal region size
3870 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3875 return STATUS_INVALID_PARAMETER
;
3879 // Get a reference to the process
3881 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3882 PROCESS_VM_OPERATION
,
3887 if (!NT_SUCCESS(Status
)) return Status
;
3892 Status
= MmFlushVirtualMemory(Process
,
3893 &CapturedBaseAddress
,
3894 &CapturedBytesToFlush
,
3898 // Release reference
3900 ObDereferenceObject(Process
);
3903 // Enter SEH to return data
3908 // Return data to user
3910 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3911 *NumberOfBytesToFlush
= 0;
3912 *IoStatusBlock
= LocalStatusBlock
;
3914 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3930 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3932 IN PVOID BaseAddress
,
3933 IN SIZE_T RegionSize
,
3934 IN PVOID
*UserAddressArray
,
3935 OUT PULONG_PTR EntriesInUserAddressArray
,
3936 OUT PULONG Granularity
)
3941 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3942 ULONG_PTR CapturedEntryCount
;
3946 // Check if we came from user mode
3948 if (PreviousMode
!= KernelMode
)
3951 // Enter SEH for probing
3956 // Catch illegal base address
3958 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3961 // Catch illegal region size
3963 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3968 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
3972 // Validate all data
3974 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3975 ProbeForWriteUlong(Granularity
);
3980 CapturedEntryCount
= *EntriesInUserAddressArray
;
3983 // Must have a count
3985 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3988 // Can't be larger than the maximum
3990 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3995 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
3999 // Probe the actual array
4001 ProbeForWrite(UserAddressArray
,
4002 CapturedEntryCount
* sizeof(PVOID
),
4005 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4008 // Get exception code
4010 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4019 CapturedEntryCount
= *EntriesInUserAddressArray
;
4020 ASSERT(CapturedEntryCount
!= 0);
4024 // Check if this is a local request
4026 if (ProcessHandle
== NtCurrentProcess())
4029 // No need to reference the process
4031 Process
= PsGetCurrentProcess();
4036 // Reference the target
4038 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4039 PROCESS_VM_OPERATION
,
4044 if (!NT_SUCCESS(Status
)) return Status
;
4048 // Compute the last address and validate it
4050 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4051 if (BaseAddress
> EndAddress
)
4056 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4057 return STATUS_INVALID_PARAMETER_4
;
4066 // Dereference if needed
4068 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4071 // Enter SEH to return data
4076 // Return data to user
4078 *EntriesInUserAddressArray
= 0;
4079 *Granularity
= PAGE_SIZE
;
4081 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4084 // Get exception code
4086 Status
= _SEH2_GetExceptionCode();
4093 return STATUS_SUCCESS
;
4101 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4102 IN PVOID BaseAddress
,
4103 IN SIZE_T RegionSize
)
4108 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4109 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4112 // Catch illegal base address
4114 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4117 // Catch illegal region size
4119 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4124 return STATUS_INVALID_PARAMETER_3
;
4128 // Check if this is a local request
4130 if (ProcessHandle
== NtCurrentProcess())
4133 // No need to reference the process
4135 Process
= PsGetCurrentProcess();
4140 // Reference the target
4142 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4143 PROCESS_VM_OPERATION
,
4148 if (!NT_SUCCESS(Status
)) return Status
;
4152 // Compute the last address and validate it
4154 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4155 if (BaseAddress
> EndAddress
)
4160 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4161 return STATUS_INVALID_PARAMETER_3
;
4170 // Dereference if needed
4172 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4177 return STATUS_SUCCESS
;
4182 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4183 IN PVOID BaseAddress
,
4184 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4185 OUT PVOID MemoryInformation
,
4186 IN SIZE_T MemoryInformationLength
,
4187 OUT PSIZE_T ReturnLength
)
4189 NTSTATUS Status
= STATUS_SUCCESS
;
4190 KPROCESSOR_MODE PreviousMode
;
4192 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4194 /* Bail out if the address is invalid */
4195 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4197 /* Probe return buffer */
4198 PreviousMode
= ExGetPreviousMode();
4199 if (PreviousMode
!= KernelMode
)
4203 ProbeForWrite(MemoryInformation
,
4204 MemoryInformationLength
,
4207 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4209 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4211 Status
= _SEH2_GetExceptionCode();
4215 if (!NT_SUCCESS(Status
))
4221 switch(MemoryInformationClass
)
4223 case MemoryBasicInformation
:
4224 /* Validate the size information of the class */
4225 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4227 /* The size is invalid */
4228 return STATUS_INFO_LENGTH_MISMATCH
;
4230 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4233 MemoryInformationLength
,
4237 case MemorySectionName
:
4238 /* Validate the size information of the class */
4239 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4241 /* The size is invalid */
4242 return STATUS_INFO_LENGTH_MISMATCH
;
4244 Status
= MiQueryMemorySectionName(ProcessHandle
,
4247 MemoryInformationLength
,
4250 case MemoryWorkingSetList
:
4251 case MemoryBasicVlmInformation
:
4253 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4265 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4266 IN OUT PVOID
* UBaseAddress
,
4267 IN ULONG_PTR ZeroBits
,
4268 IN OUT PSIZE_T URegionSize
,
4269 IN ULONG AllocationType
,
4273 PMEMORY_AREA MemoryArea
;
4274 PFN_NUMBER PageCount
;
4275 PMMVAD Vad
= NULL
, FoundVad
;
4277 PMMSUPPORT AddressSpace
;
4279 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
, HighestAddress
;
4280 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4281 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4282 PETHREAD CurrentThread
= PsGetCurrentThread();
4283 KAPC_STATE ApcState
;
4284 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4285 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4287 PMMPTE PointerPte
, PointerPde
, LastPte
;
4288 TABLE_SEARCH_RESULT Result
;
4289 PMMADDRESS_NODE Parent
;
4292 /* Check for valid Zero bits */
4293 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4295 DPRINT1("Too many zero bits\n");
4296 return STATUS_INVALID_PARAMETER_3
;
4299 /* Check for valid Allocation Types */
4300 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4301 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4303 DPRINT1("Invalid Allocation Type\n");
4304 return STATUS_INVALID_PARAMETER_5
;
4307 /* Check for at least one of these Allocation Types to be set */
4308 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4310 DPRINT1("No memory allocation base type\n");
4311 return STATUS_INVALID_PARAMETER_5
;
4314 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4315 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4317 DPRINT1("Invalid use of MEM_RESET\n");
4318 return STATUS_INVALID_PARAMETER_5
;
4321 /* Check if large pages are being used */
4322 if (AllocationType
& MEM_LARGE_PAGES
)
4324 /* Large page allocations MUST be committed */
4325 if (!(AllocationType
& MEM_COMMIT
))
4327 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4328 return STATUS_INVALID_PARAMETER_5
;
4331 /* These flags are not allowed with large page allocations */
4332 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4334 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4335 return STATUS_INVALID_PARAMETER_5
;
4339 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4340 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4342 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4343 return STATUS_INVALID_PARAMETER_5
;
4346 /* Check for valid MEM_PHYSICAL usage */
4347 if (AllocationType
& MEM_PHYSICAL
)
4349 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4350 if (!(AllocationType
& MEM_RESERVE
))
4352 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4353 return STATUS_INVALID_PARAMETER_5
;
4356 /* Only these flags are allowed with MEM_PHYSIAL */
4357 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4359 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4360 return STATUS_INVALID_PARAMETER_5
;
4363 /* Then make sure PAGE_READWRITE is used */
4364 if (Protect
!= PAGE_READWRITE
)
4366 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4367 return STATUS_INVALID_PARAMETER_6
;
4371 /* Calculate the protection mask and make sure it's valid */
4372 ProtectionMask
= MiMakeProtectionMask(Protect
);
4373 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4375 DPRINT1("Invalid protection mask\n");
4376 return STATUS_INVALID_PAGE_PROTECTION
;
4382 /* Check for user-mode parameters */
4383 if (PreviousMode
!= KernelMode
)
4385 /* Make sure they are writable */
4386 ProbeForWritePointer(UBaseAddress
);
4387 ProbeForWriteSize_t(URegionSize
);
4390 /* Capture their values */
4391 PBaseAddress
= *UBaseAddress
;
4392 PRegionSize
= *URegionSize
;
4394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4396 /* Return the exception code */
4397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4401 /* Make sure the allocation isn't past the VAD area */
4402 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4404 DPRINT1("Virtual allocation base above User Space\n");
4405 return STATUS_INVALID_PARAMETER_2
;
4408 /* Make sure the allocation wouldn't overflow past the VAD area */
4409 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4411 DPRINT1("Region size would overflow into kernel-memory\n");
4412 return STATUS_INVALID_PARAMETER_4
;
4415 /* Make sure there's a size specified */
4418 DPRINT1("Region size is invalid (zero)\n");
4419 return STATUS_INVALID_PARAMETER_4
;
4423 // If this is for the current process, just use PsGetCurrentProcess
4425 if (ProcessHandle
== NtCurrentProcess())
4427 Process
= CurrentProcess
;
4432 // Otherwise, reference the process with VM rights and attach to it if
4433 // this isn't the current process. We must attach because we'll be touching
4434 // PTEs and PDEs that belong to user-mode memory, and also touching the
4435 // Working Set which is stored in Hyperspace.
4437 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4438 PROCESS_VM_OPERATION
,
4443 if (!NT_SUCCESS(Status
)) return Status
;
4444 if (CurrentProcess
!= Process
)
4446 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4451 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4452 Process
, PBaseAddress
, ZeroBits
, PRegionSize
, AllocationType
, Protect
);
4455 // Check for large page allocations and make sure that the required privilege
4456 // is being held, before attempting to handle them.
4458 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4459 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4461 /* Fail without it */
4462 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4463 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4464 goto FailPathNoLock
;
4468 // Fail on the things we don't yet support
4470 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4472 DPRINT1("MEM_LARGE_PAGES not supported\n");
4473 Status
= STATUS_INVALID_PARAMETER
;
4474 goto FailPathNoLock
;
4476 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4478 DPRINT1("MEM_PHYSICAL not supported\n");
4479 Status
= STATUS_INVALID_PARAMETER
;
4480 goto FailPathNoLock
;
4482 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4484 DPRINT1("MEM_WRITE_WATCH not supported\n");
4485 Status
= STATUS_INVALID_PARAMETER
;
4486 goto FailPathNoLock
;
4490 // Check if the caller is reserving memory, or committing memory and letting
4491 // us pick the base address
4493 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4496 // Do not allow COPY_ON_WRITE through this API
4498 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4500 DPRINT1("Copy on write not allowed through this path\n");
4501 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4502 goto FailPathNoLock
;
4506 // Does the caller have an address in mind, or is this a blind commit?
4511 // This is a blind commit, all we need is the region size
4513 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4514 PageCount
= BYTES_TO_PAGES(PRegionSize
);
4516 StartingAddress
= 0;
4519 // Check if ZeroBits were specified
4524 // Calculate the highest address and check if it's valid
4526 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4527 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4529 Status
= STATUS_INVALID_PARAMETER_3
;
4530 goto FailPathNoLock
;
4536 // Use the highest VAD address as maximum
4538 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4544 // This is a reservation, so compute the starting address on the
4545 // expected 64KB granularity, and see where the ending address will
4546 // fall based on the aligned address and the passed in region size
4548 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4549 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4550 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
4554 // Allocate and initialize the VAD
4556 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4559 DPRINT1("Failed to allocate a VAD!\n");
4560 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4561 goto FailPathNoLock
;
4564 Vad
->u
.LongFlags
= 0;
4565 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4566 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4567 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4568 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
4571 // Lock the address space and make sure the process isn't already dead
4573 AddressSpace
= MmGetCurrentAddressSpace();
4574 MmLockAddressSpace(AddressSpace
);
4575 if (Process
->VmDeleted
)
4577 Status
= STATUS_PROCESS_IS_TERMINATING
;
4582 // Did we have a base address? If no, find a valid address that is 64KB
4583 // aligned in the VAD tree. Otherwise, make sure that the address range
4584 // which was passed in isn't already conflicting with an existing address
4589 /* Which way should we search? */
4590 if ((AllocationType
& MEM_TOP_DOWN
) || Process
->VmTopDown
)
4592 /* Find an address top-down */
4593 Result
= MiFindEmptyAddressRangeDownTree(PRegionSize
,
4602 /* Find an address bottom-up */
4603 Result
= MiFindEmptyAddressRangeInTree(PRegionSize
,
4610 if (Result
== TableFoundNode
)
4612 Status
= STATUS_NO_MEMORY
;
4617 // Now we know where the allocation ends. Make sure it doesn't end up
4618 // somewhere in kernel mode.
4620 ASSERT(StartingAddress
!= 0);
4621 ASSERT(StartingAddress
< (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
);
4622 EndingAddress
= (StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4623 ASSERT(EndingAddress
> StartingAddress
);
4624 if (EndingAddress
> HighestAddress
)
4626 Status
= STATUS_NO_MEMORY
;
4632 /* Make sure it doesn't conflict with an existing allocation */
4633 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4634 EndingAddress
>> PAGE_SHIFT
,
4637 if (Result
== TableFoundNode
)
4640 // The address specified is in conflict!
4642 Status
= STATUS_CONFLICTING_ADDRESSES
;
4648 // Write out the VAD fields for this allocation
4650 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
4651 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
4654 // FIXME: Should setup VAD bitmap
4656 Status
= STATUS_SUCCESS
;
4659 // Lock the working set and insert the VAD into the process VAD tree
4661 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4662 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4663 Process
->VadRoot
.NodeHint
= Vad
;
4664 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
4665 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4668 // Make sure the actual region size is at least as big as the
4669 // requested region size and update the value
4671 ASSERT(PRegionSize
<= (EndingAddress
+ 1 - StartingAddress
));
4672 PRegionSize
= (EndingAddress
+ 1 - StartingAddress
);
4675 // Update the virtual size of the process, and if this is now the highest
4676 // virtual size we have ever seen, update the peak virtual size to reflect
4679 Process
->VirtualSize
+= PRegionSize
;
4680 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4682 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4686 // Release address space and detach and dereference the target process if
4687 // it was different from the current process
4689 MmUnlockAddressSpace(AddressSpace
);
4690 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4691 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4694 // Use SEH to write back the base address and the region size. In the case
4695 // of an exception, we do not return back the exception code, as the memory
4696 // *has* been allocated. The caller would now have to call VirtualQuery
4697 // or do some other similar trick to actually find out where its memory
4698 // allocation ended up
4702 *URegionSize
= PRegionSize
;
4703 *UBaseAddress
= (PVOID
)StartingAddress
;
4705 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4708 // Ignore exception!
4712 DPRINT("Reserved %x bytes at %p.\n", PRegionSize
, StartingAddress
);
4713 return STATUS_SUCCESS
;
4717 // This is a MEM_COMMIT on top of an existing address which must have been
4718 // MEM_RESERVED already. Compute the start and ending base addresses based
4719 // on the user input, and then compute the actual region size once all the
4720 // alignments have been done.
4722 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4723 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4724 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4727 // Lock the address space and make sure the process isn't already dead
4729 AddressSpace
= MmGetCurrentAddressSpace();
4730 MmLockAddressSpace(AddressSpace
);
4731 if (Process
->VmDeleted
)
4733 DPRINT1("Process is dying\n");
4734 Status
= STATUS_PROCESS_IS_TERMINATING
;
4739 // Get the VAD for this address range, and make sure it exists
4741 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4742 EndingAddress
>> PAGE_SHIFT
,
4744 (PMMADDRESS_NODE
*)&FoundVad
);
4745 if (Result
!= TableFoundNode
)
4747 DPRINT1("Could not find a VAD for this allocation\n");
4748 Status
= STATUS_CONFLICTING_ADDRESSES
;
4752 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4754 /// @todo HACK: pretend success
4755 DPRINT("MEM_RESET not supported\n");
4756 Status
= STATUS_SUCCESS
;
4761 // These kinds of VADs are illegal for this Windows function when trying to
4762 // commit an existing range
4764 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4765 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4766 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4768 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4769 Status
= STATUS_CONFLICTING_ADDRESSES
;
4774 // Make sure that this address range actually fits within the VAD for it
4776 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4777 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4779 DPRINT1("Address range does not fit into the VAD\n");
4780 Status
= STATUS_CONFLICTING_ADDRESSES
;
4785 // Make sure this is an ARM3 section
4787 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4788 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4790 DPRINT1("Illegal commit of non-ARM3 section!\n");
4791 Status
= STATUS_ALREADY_COMMITTED
;
4795 // Is this a previously reserved section being committed? If so, enter the
4796 // special section path
4798 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4801 // You cannot commit large page sections through this API
4803 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4805 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4806 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4811 // You can only use caching flags on a rotate VAD
4813 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4814 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4816 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4817 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4822 // We should make sure that the section's permissions aren't being
4825 if (FoundVad
->u
.VadFlags
.NoChange
)
4828 // Make sure it's okay to touch it
4829 // Note: The Windows 2003 kernel has a bug here, passing the
4830 // unaligned base address together with the aligned size,
4831 // potentially covering a region larger than the actual allocation.
4832 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4833 // For now we keep this behavior.
4834 // TODO: analyze possible implications, create test case
4836 Status
= MiCheckSecuredVad(FoundVad
,
4840 if (!NT_SUCCESS(Status
))
4842 DPRINT1("Secured VAD being messed around with\n");
4848 // ARM3 does not support file-backed sections, only shared memory
4850 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4853 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4855 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4856 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4858 DPRINT1("Invalid page protection for rotate VAD\n");
4859 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4864 // Compute PTE addresses and the quota charge, then grab the commit lock
4866 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4867 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4868 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4869 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4872 // Get the segment template PTE and start looping each page
4874 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4875 ASSERT(TempPte
.u
.Long
!= 0);
4876 while (PointerPte
<= LastPte
)
4879 // For each non-already-committed page, write the invalid template PTE
4881 if (PointerPte
->u
.Long
== 0)
4883 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4893 // Now do the commit accounting and release the lock
4895 ASSERT(QuotaCharge
>= QuotaFree
);
4896 QuotaCharge
-= QuotaFree
;
4897 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4898 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4901 // We are done with committing the section pages
4903 Status
= STATUS_SUCCESS
;
4908 // This is a specific ReactOS check because we only use normal VADs
4910 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4913 // While this is an actual Windows check
4915 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4918 // Throw out attempts to use copy-on-write through this API path
4920 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4922 DPRINT1("Write copy attempted when not allowed\n");
4923 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4928 // Initialize a demand-zero PTE
4931 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4932 NT_ASSERT(TempPte
.u
.Long
!= 0);
4935 // Get the PTE, PDE and the last PTE for this address range
4937 PointerPde
= MiAddressToPde(StartingAddress
);
4938 PointerPte
= MiAddressToPte(StartingAddress
);
4939 LastPte
= MiAddressToPte(EndingAddress
);
4942 // Update the commit charge in the VAD as well as in the process, and check
4943 // if this commit charge was now higher than the last recorded peak, in which
4944 // case we also update the peak
4946 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4947 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4948 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4950 Process
->CommitChargePeak
= Process
->CommitCharge
;
4954 // Lock the working set while we play with user pages and page tables
4956 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4959 // Make the current page table valid, and then loop each page within it
4961 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4962 while (PointerPte
<= LastPte
)
4965 // Have we crossed into a new page table?
4967 if (MiIsPteOnPdeBoundary(PointerPte
))
4970 // Get the PDE and now make it valid too
4972 PointerPde
= MiAddressToPte(PointerPte
);
4973 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4977 // Is this a zero PTE as expected?
4979 if (PointerPte
->u
.Long
== 0)
4982 // First increment the count of pages in the page table for this
4985 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4988 // And now write the invalid demand-zero PTE as requested
4990 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4992 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4995 // If the PTE was already decommitted, there is nothing else to do
4996 // but to write the new demand-zero PTE
4998 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
5000 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
5003 // We don't handle these scenarios yet
5005 if (PointerPte
->u
.Soft
.Valid
== 0)
5007 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
5008 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
5012 // There's a change in protection, remember this for later, but do
5013 // not yet handle it.
5015 ChangeProtection
= TRUE
;
5019 // Move to the next PTE
5025 // Release the working set lock, unlock the address space, and detach from
5026 // the target process if it was not the current process. Also dereference the
5027 // target process if this wasn't the case.
5029 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5030 Status
= STATUS_SUCCESS
;
5032 MmUnlockAddressSpace(AddressSpace
);
5034 if (!NT_SUCCESS(Status
))
5038 ExFreePoolWithTag(Vad
, 'SdaV');
5043 // Check if we need to update the protection
5045 if (ChangeProtection
)
5047 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
5048 SIZE_T ProtectSize
= PRegionSize
;
5049 ULONG OldProtection
;
5052 // Change the protection of the region
5054 MiProtectVirtualMemory(Process
,
5055 &ProtectBaseAddress
,
5062 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5063 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5066 // Only write back results on success
5068 if (NT_SUCCESS(Status
))
5071 // Use SEH to write back the base address and the region size. In the case
5072 // of an exception, we strangely do return back the exception code, even
5073 // though the memory *has* been allocated. This mimics Windows behavior and
5074 // there is not much we can do about it.
5078 *URegionSize
= PRegionSize
;
5079 *UBaseAddress
= (PVOID
)StartingAddress
;
5081 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5083 Status
= _SEH2_GetExceptionCode();
5096 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5097 IN PVOID
* UBaseAddress
,
5098 IN PSIZE_T URegionSize
,
5101 PMEMORY_AREA MemoryArea
;
5104 LONG_PTR CommitReduction
= 0;
5105 ULONG_PTR StartingAddress
, EndingAddress
;
5109 PMMSUPPORT AddressSpace
;
5110 PETHREAD CurrentThread
= PsGetCurrentThread();
5111 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5112 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5113 KAPC_STATE ApcState
;
5114 BOOLEAN Attached
= FALSE
;
5118 // Only two flags are supported
5120 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5122 DPRINT1("Invalid FreeType\n");
5123 return STATUS_INVALID_PARAMETER_4
;
5127 // Check if no flag was used, or if both flags were used
5129 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5130 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5132 DPRINT1("Invalid FreeType combination\n");
5133 return STATUS_INVALID_PARAMETER_4
;
5137 // Enter SEH for probe and capture. On failure, return back to the caller
5138 // with an exception violation.
5143 // Check for user-mode parameters and make sure that they are writeable
5145 if (PreviousMode
!= KernelMode
)
5147 ProbeForWritePointer(UBaseAddress
);
5148 ProbeForWriteUlong(URegionSize
);
5152 // Capture the current values
5154 PBaseAddress
= *UBaseAddress
;
5155 PRegionSize
= *URegionSize
;
5157 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5159 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5164 // Make sure the allocation isn't past the user area
5166 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5168 DPRINT1("Virtual free base above User Space\n");
5169 return STATUS_INVALID_PARAMETER_2
;
5173 // Make sure the allocation wouldn't overflow past the user area
5175 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5177 DPRINT1("Region size would overflow into kernel-memory\n");
5178 return STATUS_INVALID_PARAMETER_3
;
5182 // If this is for the current process, just use PsGetCurrentProcess
5184 if (ProcessHandle
== NtCurrentProcess())
5186 Process
= CurrentProcess
;
5191 // Otherwise, reference the process with VM rights and attach to it if
5192 // this isn't the current process. We must attach because we'll be touching
5193 // PTEs and PDEs that belong to user-mode memory, and also touching the
5194 // Working Set which is stored in Hyperspace.
5196 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5197 PROCESS_VM_OPERATION
,
5202 if (!NT_SUCCESS(Status
)) return Status
;
5203 if (CurrentProcess
!= Process
)
5205 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5210 DPRINT("NtFreeVirtualMemory: Process 0x%p, Adress 0x%p, size 0x%x, FreeType %x.\n",
5211 Process
, PBaseAddress
, PRegionSize
, FreeType
);
5214 // Lock the address space
5216 AddressSpace
= MmGetCurrentAddressSpace();
5217 MmLockAddressSpace(AddressSpace
);
5220 // If the address space is being deleted, fail the de-allocation since it's
5221 // too late to do anything about it
5223 if (Process
->VmDeleted
)
5225 DPRINT1("Process is dead\n");
5226 Status
= STATUS_PROCESS_IS_TERMINATING
;
5231 // Compute start and end addresses, and locate the VAD
5233 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5234 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5235 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5238 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5239 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5244 // If the range exceeds the VAD's ending VPN, fail this request
5246 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5248 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5249 Status
= STATUS_UNABLE_TO_FREE_VM
;
5254 // Only private memory (except rotate VADs) can be freed through here */
5256 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5257 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5258 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5260 DPRINT1("Attempt to free section memory\n");
5261 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5266 // ARM3 does not yet handle protected VM
5268 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5271 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5272 // and that is is an ARM3 memory area, and not a section view, as we currently
5273 // don't support freeing those though this interface.
5275 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5277 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5280 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5282 if (FreeType
& MEM_RELEASE
)
5285 // ARM3 only supports this VAD in this path
5287 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5290 // Is the caller trying to remove the whole VAD, or remove only a portion
5291 // of it? If no region size is specified, then the assumption is that the
5292 // whole VAD is to be destroyed
5297 // The caller must specify the base address identically to the range
5298 // that is stored in the VAD.
5300 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5302 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5303 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5308 // Now compute the actual start/end addresses based on the VAD
5310 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5311 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5314 // Finally lock the working set and remove the VAD from the VAD tree
5316 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5317 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5318 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5323 // This means the caller wants to release a specific region within
5324 // the range. We have to find out which range this is -- the following
5325 // possibilities exist plus their union (CASE D):
5327 // STARTING ADDRESS ENDING ADDRESS
5328 // [<========][========================================][=========>]
5329 // CASE A CASE B CASE C
5332 // First, check for case A or D
5334 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5339 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5342 // This is the easiest one to handle -- it is identical to
5343 // the code path above when the caller sets a zero region size
5344 // and the whole VAD is destroyed
5346 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5347 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5348 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5353 // This case is pretty easy too -- we compute a bunch of
5354 // pages to decommit, and then push the VAD's starting address
5355 // a bit further down, then decrement the commit charge
5357 // NOT YET IMPLEMENTED IN ARM3.
5359 DPRINT1("Case A not handled\n");
5360 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5364 // After analyzing the VAD, set it to NULL so that we don't
5365 // free it in the exit path
5373 // This is case B or case C. First check for case C
5375 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5377 PMEMORY_AREA MemoryArea
;
5380 // This is pretty easy and similar to case A. We compute the
5381 // amount of pages to decommit, update the VAD's commit charge
5382 // and then change the ending address of the VAD to be a bit
5385 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5386 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5390 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5391 // For ReactOS: shrink the corresponding memory area
5392 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5393 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
5394 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
5395 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
5396 MemoryArea
->EndingAddress
= (PVOID
)(StartingAddress
);
5401 // This is case B and the hardest one. Because we are removing
5402 // a chunk of memory from the very middle of the VAD, we must
5403 // actually split the VAD into two new VADs and compute the
5404 // commit charges for each of them, and reinsert new charges.
5406 // NOT YET IMPLEMENTED IN ARM3.
5408 DPRINT1("Case B not handled\n");
5409 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5414 // After analyzing the VAD, set it to NULL so that we don't
5415 // free it in the exit path
5422 // Now we have a range of pages to dereference, so call the right API
5423 // to do that and then release the working set, since we're done messing
5424 // around with process pages.
5426 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5427 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5428 Status
= STATUS_SUCCESS
;
5432 // Update the process counters
5434 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5435 Process
->CommitCharge
-= CommitReduction
;
5436 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5439 // Unlock the address space and free the VAD in failure cases. Next,
5440 // detach from the target process so we can write the region size and the
5441 // base address to the correct source process, and dereference the target
5444 MmUnlockAddressSpace(AddressSpace
);
5445 if (Vad
) ExFreePool(Vad
);
5446 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5447 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5450 // Use SEH to safely return the region size and the base address of the
5451 // deallocation. If we get an access violation, don't return a failure code
5452 // as the deallocation *has* happened. The caller will just have to figure
5453 // out another way to find out where it is (such as VirtualQuery).
5457 *URegionSize
= PRegionSize
;
5458 *UBaseAddress
= (PVOID
)StartingAddress
;
5460 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5468 // This is the decommit path. You cannot decommit from the following VADs in
5469 // Windows, so fail the vall
5471 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5472 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5473 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5475 DPRINT1("Trying to decommit from invalid VAD\n");
5476 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5481 // If the caller did not specify a region size, first make sure that this
5482 // region is actually committed. If it is, then compute the ending address
5483 // based on the VAD.
5487 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5489 DPRINT1("Decomitting non-committed memory\n");
5490 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5493 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5497 // Decommit the PTEs for the range plus the actual backing pages for the
5498 // range, then reduce that amount from the commit charge in the VAD
5500 CommitReduction
= MiAddressToPte(EndingAddress
) -
5501 MiAddressToPte(StartingAddress
) +
5503 MiDecommitPages((PVOID
)StartingAddress
,
5504 MiAddressToPte(EndingAddress
),
5507 ASSERT(CommitReduction
>= 0);
5508 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5509 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5512 // We are done, go to the exit path without freeing the VAD as it remains
5513 // valid since we have not released the allocation.
5516 Status
= STATUS_SUCCESS
;
5520 // In the failure path, we detach and derefernece the target process, and
5521 // return whatever failure code was sent.
5524 MmUnlockAddressSpace(AddressSpace
);
5525 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5526 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5533 MmGetPhysicalAddress(PVOID Address
)
5535 PHYSICAL_ADDRESS PhysicalAddress
;
5539 /* Check if the PXE/PPE/PDE is valid */
5541 #if (_MI_PAGING_LEVELS == 4)
5542 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5544 #if (_MI_PAGING_LEVELS >= 3)
5545 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5547 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5549 /* Check for large pages */
5550 TempPde
= *MiAddressToPde(Address
);
5551 if (TempPde
.u
.Hard
.LargePage
)
5553 /* Physical address is base page + large page offset */
5554 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5555 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5556 return PhysicalAddress
;
5559 /* Check if the PTE is valid */
5560 TempPte
= *MiAddressToPte(Address
);
5561 if (TempPte
.u
.Hard
.Valid
)
5563 /* Physical address is base page + page offset */
5564 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5565 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5566 return PhysicalAddress
;
5570 KeRosDumpStackFrames(NULL
, 20);
5571 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5572 PhysicalAddress
.QuadPart
= 0;
5573 return PhysicalAddress
;