2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
18 #define MI_MAPPED_COPY_PAGES 14
19 #define MI_POOL_COPY_BYTES 512
20 #define MI_MAX_TRANSFER_SIZE 64 * 1024
23 MiProtectVirtualMemory(IN PEPROCESS Process
,
24 IN OUT PVOID
*BaseAddress
,
25 IN OUT PSIZE_T NumberOfBytesToProtect
,
26 IN ULONG NewAccessProtection
,
27 OUT PULONG OldAccessProtection OPTIONAL
);
31 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
33 IN ULONG ProtectionMask
,
35 IN BOOLEAN CaptureDirtyBit
);
38 /* PRIVATE FUNCTIONS **********************************************************/
42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
43 IN ULONG_PTR EndingAddress
,
47 PMMPTE PointerPte
, LastPte
;
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
= MiPteToPde(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
= MiPteToPde(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
= MiPteToPde(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
= MiPteToPde(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 MiReleasePfnLock(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
= MiAcquirePfnLock();
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
= MiAcquirePfnLock();
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 MiReleasePfnLock(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 MI_ASSERT_PFN_LOCK_HELD();
404 /* Capture the PTE */
405 TempPte
= *PointerPte
;
407 /* See if the PTE is valid */
408 if (TempPte
.u
.Hard
.Valid
== 0)
410 /* Prototype and paged out PTEs not supported yet */
411 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
412 ASSERT((TempPte
.u
.Soft
.PageFileHigh
== 0) || (TempPte
.u
.Soft
.Transition
== 1));
414 if (TempPte
.u
.Soft
.Transition
)
416 /* Get the PFN entry */
417 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
418 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
420 DPRINT("Pte %p is transitional!\n", PointerPte
);
422 /* Make sure the saved PTE address is valid */
423 ASSERT((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) == PointerPte
);
425 /* Destroy the PTE */
426 MI_ERASE_PTE(PointerPte
);
428 /* Drop the reference on the page table. */
429 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
431 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
433 /* Make the page free. For prototypes, it will be made free when deleting the section object */
434 if (Pfn1
->u3
.e2
.ReferenceCount
== 0)
436 /* And it should be in standby or modified list */
437 ASSERT((Pfn1
->u3
.e1
.PageLocation
== ModifiedPageList
) || (Pfn1
->u3
.e1
.PageLocation
== StandbyPageList
));
439 /* Unlink it and set its reference count to one */
440 MiUnlinkPageFromList(Pfn1
);
441 Pfn1
->u3
.e2
.ReferenceCount
++;
443 /* This will put it back in free list and clean properly up */
444 MI_SET_PFN_DELETED(Pfn1
);
445 MiDecrementReferenceCount(Pfn1
, PageFrameIndex
);
451 /* Get the PFN entry */
452 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
453 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
455 /* Check if this is a valid, prototype PTE */
456 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
458 /* Get the PDE and make sure it's faulted in */
459 PointerPde
= MiPteToPde(PointerPte
);
460 if (PointerPde
->u
.Hard
.Valid
== 0)
462 #if (_MI_PAGING_LEVELS == 2)
463 /* Could be paged pool access from a new process -- synchronize the page directories */
464 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
467 /* The PDE must be valid at this point */
468 KeBugCheckEx(MEMORY_MANAGEMENT
,
470 (ULONG_PTR
)PointerPte
,
472 (ULONG_PTR
)VirtualAddress
);
474 #if (_MI_PAGING_LEVELS == 2)
477 /* Drop the share count on the page table */
478 PointerPde
= MiPteToPde(PointerPte
);
479 MiDecrementShareCount(MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
),
480 PointerPde
->u
.Hard
.PageFrameNumber
);
482 /* Drop the share count */
483 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
485 /* Either a fork, or this is the shared user data page */
486 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
488 /* If it's not the shared user page, then crash, since there's no fork() yet */
489 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
490 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
492 /* Must be some sort of memory corruption */
493 KeBugCheckEx(MEMORY_MANAGEMENT
,
495 (ULONG_PTR
)PointerPte
,
496 (ULONG_PTR
)PrototypePte
,
497 (ULONG_PTR
)Pfn1
->PteAddress
);
502 MI_ERASE_PTE(PointerPte
);
506 /* Make sure the saved PTE address is valid */
507 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
509 /* The PFN entry is illegal, or invalid */
510 KeBugCheckEx(MEMORY_MANAGEMENT
,
512 (ULONG_PTR
)PointerPte
,
514 (ULONG_PTR
)Pfn1
->PteAddress
);
518 MI_ERASE_PTE(PointerPte
);
520 /* There should only be 1 shared reference count */
521 ASSERT(Pfn1
->u2
.ShareCount
== 1);
523 /* Drop the reference on the page table. */
524 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
526 /* Mark the PFN for deletion and dereference what should be the last ref */
527 MI_SET_PFN_DELETED(Pfn1
);
528 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
530 /* We should eventually do this */
531 //CurrentProcess->NumberOfPrivatePages--;
540 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
541 IN ULONG_PTR EndingAddress
,
544 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
547 PEPROCESS CurrentProcess
;
549 BOOLEAN AddressGap
= FALSE
;
550 PSUBSECTION Subsection
;
552 /* Get out if this is a fake VAD, RosMm will free the marea pages */
553 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
555 /* Grab the process and PTE/PDE for the address being deleted */
556 CurrentProcess
= PsGetCurrentProcess();
557 PointerPde
= MiAddressToPde(Va
);
558 PointerPte
= MiAddressToPte(Va
);
560 /* Check if this is a section VAD or a VM VAD */
561 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
563 /* Don't worry about prototypes */
564 PrototypePte
= LastPrototypePte
= NULL
;
568 /* Get the prototype PTE */
569 PrototypePte
= Vad
->FirstPrototypePte
;
570 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
573 /* In all cases, we don't support fork() yet */
574 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
576 /* Loop the PTE for each VA */
579 /* First keep going until we find a valid PDE */
580 while (!PointerPde
->u
.Long
)
582 /* There are gaps in the address space */
585 /* Still no valid PDE, try the next 4MB (or whatever) */
588 /* Update the PTE on this new boundary */
589 PointerPte
= MiPteToAddress(PointerPde
);
591 /* Check if all the PDEs are invalid, so there's nothing to free */
592 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
593 if (Va
> EndingAddress
) return;
596 /* Now check if the PDE is mapped in */
597 if (!PointerPde
->u
.Hard
.Valid
)
599 /* It isn't, so map it in */
600 PointerPte
= MiPteToAddress(PointerPde
);
601 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
604 /* Now we should have a valid PDE, mapped in, and still have some VA */
605 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
606 ASSERT(Va
<= EndingAddress
);
608 /* Check if this is a section VAD with gaps in it */
609 if ((AddressGap
) && (LastPrototypePte
))
611 /* We need to skip to the next correct prototype PTE */
612 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
614 /* And we need the subsection to skip to the next last prototype PTE */
615 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
619 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
623 /* No more subsections, we are done with prototype PTEs */
628 /* Lock the PFN Database while we delete the PTEs */
629 OldIrql
= MiAcquirePfnLock();
632 /* Capture the PDE and make sure it exists */
633 TempPte
= *PointerPte
;
636 MiDecrementPageTableReferences((PVOID
)Va
);
638 /* Check if the PTE is actually mapped in */
639 if (MI_IS_MAPPED_PTE(&TempPte
))
641 /* Are we dealing with section VAD? */
642 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
644 /* We need to skip to the next correct prototype PTE */
645 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
647 /* And we need the subsection to skip to the next last prototype PTE */
648 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
652 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
656 /* No more subsections, we are done with prototype PTEs */
661 /* Check for prototype PTE */
662 if ((TempPte
.u
.Hard
.Valid
== 0) &&
663 (TempPte
.u
.Soft
.Prototype
== 1))
666 MI_ERASE_PTE(PointerPte
);
670 /* Delete the PTE proper */
671 MiDeletePte(PointerPte
,
679 /* The PTE was never mapped, just nuke it here */
680 MI_ERASE_PTE(PointerPte
);
684 /* Update the address and PTE for it */
689 /* Making sure the PDE is still valid */
690 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
692 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
694 /* The PDE should still be valid at this point */
695 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
697 /* Check remaining PTE count (go back 1 page due to above loop) */
698 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
700 if (PointerPde
->u
.Long
!= 0)
702 /* Delete the PTE proper */
703 MiDeletePte(PointerPde
,
704 MiPteToAddress(PointerPde
),
710 /* Release the lock and get out if we're done */
711 MiReleasePfnLock(OldIrql
);
712 if (Va
> EndingAddress
) return;
714 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
715 PointerPde
= MiAddressToPde(Va
);
721 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
722 OUT PBOOLEAN HaveBadAddress
,
723 OUT PULONG_PTR BadAddress
)
725 PEXCEPTION_RECORD ExceptionRecord
;
731 *HaveBadAddress
= FALSE
;
734 // Get the exception record
736 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
739 // Look at the exception code
741 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
742 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
743 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
746 // We can tell the address if we have more than one parameter
748 if (ExceptionRecord
->NumberParameters
> 1)
751 // Return the address
753 *HaveBadAddress
= TRUE
;
754 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
759 // Continue executing the next handler
761 return EXCEPTION_EXECUTE_HANDLER
;
766 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
767 IN PVOID SourceAddress
,
768 IN PEPROCESS TargetProcess
,
769 OUT PVOID TargetAddress
,
770 IN SIZE_T BufferSize
,
771 IN KPROCESSOR_MODE PreviousMode
,
772 OUT PSIZE_T ReturnSize
)
774 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
775 PMDL Mdl
= (PMDL
)MdlBuffer
;
776 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
777 volatile BOOLEAN FailedInProbe
= FALSE
;
778 volatile BOOLEAN PagesLocked
= FALSE
;
779 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
780 volatile PVOID MdlAddress
= NULL
;
782 BOOLEAN HaveBadAddress
;
783 ULONG_PTR BadAddress
;
784 NTSTATUS Status
= STATUS_SUCCESS
;
788 // Calculate the maximum amount of data to move
790 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
791 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
792 CurrentSize
= TotalSize
;
793 RemainingSize
= BufferSize
;
796 // Loop as long as there is still data
798 while (RemainingSize
> 0)
801 // Check if this transfer will finish everything off
803 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
806 // Attach to the source address space
808 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
811 // Check state for this pass
813 ASSERT(MdlAddress
== NULL
);
814 ASSERT(PagesLocked
== FALSE
);
815 ASSERT(FailedInProbe
== FALSE
);
818 // Protect user-mode copy
823 // If this is our first time, probe the buffer
825 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
828 // Catch a failure here
830 FailedInProbe
= TRUE
;
835 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
840 FailedInProbe
= FALSE
;
844 // Initialize and probe and lock the MDL
846 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
847 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
850 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
852 Status
= _SEH2_GetExceptionCode();
856 /* Detach from source process */
857 KeUnstackDetachProcess(&ApcState
);
859 if (Status
!= STATUS_SUCCESS
)
867 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
875 Status
= STATUS_INSUFFICIENT_RESOURCES
;
880 // Grab to the target process
882 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
887 // Check if this is our first time through
889 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
892 // Catch a failure here
894 FailedInProbe
= TRUE
;
899 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
904 FailedInProbe
= FALSE
;
908 // Now do the actual move
910 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
912 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
916 *ReturnSize
= BufferSize
- RemainingSize
;
918 // Check if we failed during the probe
925 Status
= _SEH2_GetExceptionCode();
930 // Othewise we failed during the move.
931 // Check if we know exactly where we stopped copying
936 // Return the exact number of bytes copied
938 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
941 // Return partial copy
943 Status
= STATUS_PARTIAL_COPY
;
948 /* Detach from target process */
949 KeUnstackDetachProcess(&ApcState
);
952 // Check for SEH status
954 if (Status
!= STATUS_SUCCESS
)
962 MmUnmapLockedPages(MdlAddress
, Mdl
);
968 // Update location and size
970 RemainingSize
-= CurrentSize
;
971 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
972 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
976 if (MdlAddress
!= NULL
)
977 MmUnmapLockedPages(MdlAddress
, Mdl
);
984 if (Status
== STATUS_SUCCESS
)
985 *ReturnSize
= BufferSize
;
991 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
992 IN PVOID SourceAddress
,
993 IN PEPROCESS TargetProcess
,
994 OUT PVOID TargetAddress
,
995 IN SIZE_T BufferSize
,
996 IN KPROCESSOR_MODE PreviousMode
,
997 OUT PSIZE_T ReturnSize
)
999 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
1000 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
1001 volatile BOOLEAN FailedInProbe
= FALSE
, HavePoolAddress
= FALSE
;
1002 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
1004 KAPC_STATE ApcState
;
1005 BOOLEAN HaveBadAddress
;
1006 ULONG_PTR BadAddress
;
1007 NTSTATUS Status
= STATUS_SUCCESS
;
1010 DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1011 BufferSize
, SourceProcess
, SourceAddress
, TargetProcess
, TargetAddress
);
1014 // Calculate the maximum amount of data to move
1016 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1017 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1018 CurrentSize
= TotalSize
;
1019 RemainingSize
= BufferSize
;
1022 // Check if we can use the stack
1024 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1029 PoolAddress
= (PVOID
)StackBuffer
;
1036 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1037 if (!PoolAddress
) ASSERT(FALSE
);
1038 HavePoolAddress
= TRUE
;
1042 // Loop as long as there is still data
1044 while (RemainingSize
> 0)
1047 // Check if this transfer will finish everything off
1049 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1052 // Attach to the source address space
1054 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1056 /* Check that state is sane */
1057 ASSERT(FailedInProbe
== FALSE
);
1058 ASSERT(Status
== STATUS_SUCCESS
);
1061 // Protect user-mode copy
1066 // If this is our first time, probe the buffer
1068 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1071 // Catch a failure here
1073 FailedInProbe
= TRUE
;
1078 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1083 FailedInProbe
= FALSE
;
1089 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1091 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1095 *ReturnSize
= BufferSize
- RemainingSize
;
1098 // Check if we failed during the probe
1105 Status
= _SEH2_GetExceptionCode();
1110 // We failed during the move.
1111 // Check if we know exactly where we stopped copying
1116 // Return the exact number of bytes copied
1118 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1121 // Return partial copy
1123 Status
= STATUS_PARTIAL_COPY
;
1128 /* Let go of the source */
1129 KeUnstackDetachProcess(&ApcState
);
1131 if (Status
!= STATUS_SUCCESS
)
1136 /* Grab the target process */
1137 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1142 // Check if this is our first time through
1144 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
1147 // Catch a failure here
1149 FailedInProbe
= TRUE
;
1154 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1159 FailedInProbe
= FALSE
;
1163 // Now do the actual move
1165 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1167 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1171 *ReturnSize
= BufferSize
- RemainingSize
;
1173 // Check if we failed during the probe
1180 Status
= _SEH2_GetExceptionCode();
1185 // Otherwise we failed during the move.
1186 // Check if we know exactly where we stopped copying
1191 // Return the exact number of bytes copied
1193 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1196 // Return partial copy
1198 Status
= STATUS_PARTIAL_COPY
;
1204 // Detach from target
1206 KeUnstackDetachProcess(&ApcState
);
1209 // Check for SEH status
1211 if (Status
!= STATUS_SUCCESS
)
1217 // Update location and size
1219 RemainingSize
-= CurrentSize
;
1220 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1221 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1227 // Check if we had allocated pool
1229 if (HavePoolAddress
)
1230 ExFreePoolWithTag(PoolAddress
, 'VmRw');
1235 if (Status
== STATUS_SUCCESS
)
1236 *ReturnSize
= BufferSize
;
1242 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1243 IN PVOID SourceAddress
,
1244 IN PEPROCESS TargetProcess
,
1245 OUT PVOID TargetAddress
,
1246 IN SIZE_T BufferSize
,
1247 IN KPROCESSOR_MODE PreviousMode
,
1248 OUT PSIZE_T ReturnSize
)
1251 PEPROCESS Process
= SourceProcess
;
1254 // Don't accept zero-sized buffers
1256 if (!BufferSize
) return STATUS_SUCCESS
;
1259 // If we are copying from ourselves, lock the target instead
1261 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1264 // Acquire rundown protection
1266 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1271 return STATUS_PROCESS_IS_TERMINATING
;
1275 // See if we should use the pool copy
1277 if (BufferSize
> MI_POOL_COPY_BYTES
)
1282 Status
= MiDoMappedCopy(SourceProcess
,
1295 Status
= MiDoPoolCopy(SourceProcess
,
1307 ExReleaseRundownProtection(&Process
->RundownProtect
);
1313 MmFlushVirtualMemory(IN PEPROCESS Process
,
1314 IN OUT PVOID
*BaseAddress
,
1315 IN OUT PSIZE_T RegionSize
,
1316 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1324 return STATUS_SUCCESS
;
1329 MiGetPageProtection(IN PMMPTE PointerPte
)
1333 PEPROCESS CurrentProcess
;
1334 PETHREAD CurrentThread
;
1335 BOOLEAN WsSafe
, WsShared
;
1340 /* Copy this PTE's contents */
1341 TempPte
= *PointerPte
;
1343 /* Assure it's not totally zero */
1344 ASSERT(TempPte
.u
.Long
);
1346 /* Check for a special prototype format */
1347 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1348 (TempPte
.u
.Soft
.Prototype
== 1))
1350 /* Check if the prototype PTE is not yet pointing to a PTE */
1351 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1353 /* The prototype PTE contains the protection */
1354 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1357 /* Get a pointer to the underlying shared PTE */
1358 PointerPte
= MiProtoPteToPte(&TempPte
);
1360 /* Since the PTE we want to read can be paged out at any time, we need
1361 to release the working set lock first, so that it can be paged in */
1362 CurrentThread
= PsGetCurrentThread();
1363 CurrentProcess
= PsGetCurrentProcess();
1364 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1369 /* Now read the PTE value */
1370 TempPte
= *PointerPte
;
1372 /* Check if that one is invalid */
1373 if (!TempPte
.u
.Hard
.Valid
)
1375 /* We get the protection directly from this PTE */
1376 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1380 /* The PTE is valid, so we might need to get the protection from
1381 the PFN. Lock the PFN database */
1382 OldIrql
= MiAcquirePfnLock();
1384 /* Check if the PDE is still valid */
1385 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1387 /* It's not, make it valid */
1388 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1391 /* Now it's safe to read the PTE value again */
1392 TempPte
= *PointerPte
;
1393 ASSERT(TempPte
.u
.Long
!= 0);
1395 /* Check again if the PTE is invalid */
1396 if (!TempPte
.u
.Hard
.Valid
)
1398 /* The PTE is not valid, so we can use it's protection field */
1399 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1403 /* The PTE is valid, so we can find the protection in the
1404 OriginalPte field of the PFN */
1405 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1406 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1409 /* Release the PFN database */
1410 MiReleasePfnLock(OldIrql
);
1413 /* Lock the working set again */
1414 MiLockProcessWorkingSetForFault(CurrentProcess
,
1422 /* In the easy case of transition or demand zero PTE just return its protection */
1423 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1425 /* If we get here, the PTE is valid, so look up the page in PFN database */
1426 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1427 if (!Pfn
->u3
.e1
.PrototypePte
)
1429 /* Return protection of the original pte */
1430 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1431 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1434 /* This is software PTE */
1435 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1436 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1437 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1438 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1439 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1444 MiQueryAddressState(IN PVOID Va
,
1446 IN PEPROCESS TargetProcess
,
1447 OUT PULONG ReturnedProtect
,
1451 PMMPTE PointerPte
, ProtoPte
;
1453 #if (_MI_PAGING_LEVELS >= 3)
1456 #if (_MI_PAGING_LEVELS >= 4)
1459 MMPTE TempPte
, TempProtoPte
;
1460 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1461 ULONG State
= MEM_RESERVE
, Protect
= 0;
1462 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1463 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1465 /* Only normal VADs supported */
1466 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1468 /* Get the PDE and PTE for the address */
1469 PointerPde
= MiAddressToPde(Va
);
1470 PointerPte
= MiAddressToPte(Va
);
1471 #if (_MI_PAGING_LEVELS >= 3)
1472 PointerPpe
= MiAddressToPpe(Va
);
1474 #if (_MI_PAGING_LEVELS >= 4)
1475 PointerPxe
= MiAddressToPxe(Va
);
1478 /* Return the next range */
1479 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1483 #if (_MI_PAGING_LEVELS >= 4)
1484 /* Does the PXE exist? */
1485 if (PointerPxe
->u
.Long
== 0)
1487 /* It does not, next range starts at the next PXE */
1488 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1492 /* Is the PXE valid? */
1493 if (PointerPxe
->u
.Hard
.Valid
== 0)
1495 /* Is isn't, fault it in (make the PPE accessible) */
1496 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1499 #if (_MI_PAGING_LEVELS >= 3)
1500 /* Does the PPE exist? */
1501 if (PointerPpe
->u
.Long
== 0)
1503 /* It does not, next range starts at the next PPE */
1504 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1508 /* Is the PPE valid? */
1509 if (PointerPpe
->u
.Hard
.Valid
== 0)
1511 /* Is isn't, fault it in (make the PDE accessible) */
1512 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1516 /* Does the PDE exist? */
1517 if (PointerPde
->u
.Long
== 0)
1519 /* It does not, next range starts at the next PDE */
1520 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1524 /* Is the PDE valid? */
1525 if (PointerPde
->u
.Hard
.Valid
== 0)
1527 /* Is isn't, fault it in (make the PTE accessible) */
1528 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1531 /* We have a PTE that we can access now! */
1536 /* Is it safe to try reading the PTE? */
1539 /* FIXME: watch out for large pages */
1540 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1542 /* Capture the PTE */
1543 TempPte
= *PointerPte
;
1544 if (TempPte
.u
.Long
!= 0)
1546 /* The PTE is valid, so it's not zeroed out */
1547 DemandZeroPte
= FALSE
;
1549 /* Is it a decommited, invalid, or faulted PTE? */
1550 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1551 (TempPte
.u
.Hard
.Valid
== 0) &&
1552 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1553 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1555 /* Otherwise our defaults should hold */
1556 ASSERT(Protect
== 0);
1557 ASSERT(State
== MEM_RESERVE
);
1561 /* This means it's committed */
1564 /* We don't support these */
1565 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1566 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1567 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1569 /* Get protection state of this page */
1570 Protect
= MiGetPageProtection(PointerPte
);
1572 /* Check if this is an image-backed VAD */
1573 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1574 (TempPte
.u
.Soft
.Prototype
== 1) &&
1575 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1578 DPRINT1("Not supported\n");
1585 /* Check if this was a demand-zero PTE, since we need to find the state */
1588 /* Not yet handled */
1589 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1590 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1592 /* Check if this is private commited memory, or an section-backed VAD */
1593 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1595 /* Tell caller about the next range */
1596 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1598 /* Get the prototype PTE for this VAD */
1599 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1600 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1603 /* We should unlock the working set, but it's not being held! */
1605 /* Is the prototype PTE actually valid (committed)? */
1606 TempProtoPte
= *ProtoPte
;
1607 if (TempProtoPte
.u
.Long
)
1609 /* Unless this is a memory-mapped file, handle it like private VAD */
1611 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1612 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1615 /* We should re-lock the working set */
1618 else if (Vad
->u
.VadFlags
.MemCommit
)
1620 /* This is committed memory */
1623 /* Convert the protection */
1624 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1628 /* Return the protection code */
1629 *ReturnedProtect
= Protect
;
1635 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1636 IN PVOID BaseAddress
,
1637 OUT PVOID MemoryInformation
,
1638 IN SIZE_T MemoryInformationLength
,
1639 OUT PSIZE_T ReturnLength
)
1641 PEPROCESS TargetProcess
;
1642 NTSTATUS Status
= STATUS_SUCCESS
;
1644 PVOID Address
, NextAddress
;
1645 BOOLEAN Found
= FALSE
;
1646 ULONG NewProtect
, NewState
;
1648 MEMORY_BASIC_INFORMATION MemoryInfo
;
1649 KAPC_STATE ApcState
;
1650 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1651 PMEMORY_AREA MemoryArea
;
1652 SIZE_T ResultLength
;
1654 /* Check for illegal addresses in user-space, or the shared memory area */
1655 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1656 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1658 Address
= PAGE_ALIGN(BaseAddress
);
1660 /* Make up an info structure describing this range */
1661 MemoryInfo
.BaseAddress
= Address
;
1662 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1663 MemoryInfo
.Type
= MEM_PRIVATE
;
1665 /* Special case for shared data */
1666 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1668 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1669 MemoryInfo
.State
= MEM_COMMIT
;
1670 MemoryInfo
.Protect
= PAGE_READONLY
;
1671 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1675 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1676 MemoryInfo
.State
= MEM_RESERVE
;
1677 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1678 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1681 /* Return the data, NtQueryInformation already probed it*/
1682 if (PreviousMode
!= KernelMode
)
1686 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1687 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1691 Status
= _SEH2_GetExceptionCode();
1697 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1698 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1704 /* Check if this is for a local or remote process */
1705 if (ProcessHandle
== NtCurrentProcess())
1707 TargetProcess
= PsGetCurrentProcess();
1711 /* Reference the target process */
1712 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1713 PROCESS_QUERY_INFORMATION
,
1715 ExGetPreviousMode(),
1716 (PVOID
*)&TargetProcess
,
1718 if (!NT_SUCCESS(Status
)) return Status
;
1720 /* Attach to it now */
1721 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1724 /* Lock the address space and make sure the process isn't already dead */
1725 MmLockAddressSpace(&TargetProcess
->Vm
);
1726 if (TargetProcess
->VmDeleted
)
1728 /* Unlock the address space of the process */
1729 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1731 /* Check if we were attached */
1732 if (ProcessHandle
!= NtCurrentProcess())
1734 /* Detach and dereference the process */
1735 KeUnstackDetachProcess(&ApcState
);
1736 ObDereferenceObject(TargetProcess
);
1740 DPRINT1("Process is dying\n");
1741 return STATUS_PROCESS_IS_TERMINATING
;
1745 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1746 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1748 /* Scan on the right */
1749 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1750 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1753 /* Check if this VAD covers the allocation range */
1754 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1755 (BaseVpn
<= Vad
->EndingVpn
))
1762 /* Check if this VAD is too high */
1763 if (BaseVpn
< Vad
->StartingVpn
)
1765 /* Stop if there is no left child */
1766 if (!Vad
->LeftChild
) break;
1768 /* Search on the left next */
1769 Vad
= Vad
->LeftChild
;
1773 /* Then this VAD is too low, keep searching on the right */
1774 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1776 /* Stop if there is no right child */
1777 if (!Vad
->RightChild
) break;
1779 /* Search on the right next */
1780 Vad
= Vad
->RightChild
;
1785 /* Was a VAD found? */
1788 Address
= PAGE_ALIGN(BaseAddress
);
1790 /* Calculate region size */
1793 if (Vad
->StartingVpn
>= BaseVpn
)
1795 /* Region size is the free space till the start of that VAD */
1796 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1800 /* Get the next VAD */
1801 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1804 /* Region size is the free space till the start of that VAD */
1805 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1809 /* Maximum possible region size with that base address */
1810 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1816 /* Maximum possible region size with that base address */
1817 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1820 /* Unlock the address space of the process */
1821 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1823 /* Check if we were attached */
1824 if (ProcessHandle
!= NtCurrentProcess())
1826 /* Detach and derefernece the process */
1827 KeUnstackDetachProcess(&ApcState
);
1828 ObDereferenceObject(TargetProcess
);
1831 /* Build the rest of the initial information block */
1832 MemoryInfo
.BaseAddress
= Address
;
1833 MemoryInfo
.AllocationBase
= NULL
;
1834 MemoryInfo
.AllocationProtect
= 0;
1835 MemoryInfo
.State
= MEM_FREE
;
1836 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1837 MemoryInfo
.Type
= 0;
1839 /* Return the data, NtQueryInformation already probed it*/
1840 if (PreviousMode
!= KernelMode
)
1844 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1845 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1849 Status
= _SEH2_GetExceptionCode();
1855 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1856 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1862 /* Set the correct memory type based on what kind of VAD this is */
1863 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1864 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1866 MemoryInfo
.Type
= MEM_PRIVATE
;
1868 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1870 MemoryInfo
.Type
= MEM_IMAGE
;
1874 MemoryInfo
.Type
= MEM_MAPPED
;
1877 /* Find the memory area the specified address belongs to */
1878 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1879 ASSERT(MemoryArea
!= NULL
);
1881 /* Determine information dependent on the memory area type */
1882 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1884 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1885 if (!NT_SUCCESS(Status
))
1887 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1888 MemoryArea
, MA_GetStartingAddress(MemoryArea
), MA_GetEndingAddress(MemoryArea
), BaseAddress
);
1889 ASSERT(NT_SUCCESS(Status
));
1894 /* Build the initial information block */
1895 Address
= PAGE_ALIGN(BaseAddress
);
1896 MemoryInfo
.BaseAddress
= Address
;
1897 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1898 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1899 MemoryInfo
.Type
= MEM_PRIVATE
;
1901 /* Acquire the working set lock (shared is enough) */
1902 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1904 /* Find the largest chunk of memory which has the same state and protection mask */
1905 MemoryInfo
.State
= MiQueryAddressState(Address
,
1908 &MemoryInfo
.Protect
,
1910 Address
= NextAddress
;
1911 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1913 /* Keep going unless the state or protection mask changed */
1914 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1915 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1916 Address
= NextAddress
;
1919 /* Release the working set lock */
1920 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1922 /* Check if we went outside of the VAD */
1923 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1925 /* Set the end of the VAD as the end address */
1926 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1929 /* Now that we know the last VA address, calculate the region size */
1930 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1933 /* Unlock the address space of the process */
1934 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1936 /* Check if we were attached */
1937 if (ProcessHandle
!= NtCurrentProcess())
1939 /* Detach and derefernece the process */
1940 KeUnstackDetachProcess(&ApcState
);
1941 ObDereferenceObject(TargetProcess
);
1944 /* Return the data, NtQueryInformation already probed it */
1945 if (PreviousMode
!= KernelMode
)
1949 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1950 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1952 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1954 Status
= _SEH2_GetExceptionCode();
1960 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1961 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1965 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1966 "State: %lx Type: %lx Size: %lx\n",
1967 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1968 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1969 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1976 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1977 IN ULONG_PTR EndingAddress
,
1979 IN PEPROCESS Process
)
1981 PMMPTE PointerPte
, LastPte
;
1983 BOOLEAN OnBoundary
= TRUE
;
1986 /* Get the PDE and PTE addresses */
1987 PointerPde
= MiAddressToPde(StartingAddress
);
1988 PointerPte
= MiAddressToPte(StartingAddress
);
1989 LastPte
= MiAddressToPte(EndingAddress
);
1991 /* Loop all the PTEs */
1992 while (PointerPte
<= LastPte
)
1994 /* Check if we've hit an new PDE boundary */
1997 /* Is this PDE demand zero? */
1998 PointerPde
= MiPteToPde(PointerPte
);
1999 if (PointerPde
->u
.Long
!= 0)
2001 /* It isn't -- is it valid? */
2002 if (PointerPde
->u
.Hard
.Valid
== 0)
2004 /* Nope, fault it in */
2005 MiMakeSystemAddressValid(PointerPte
, Process
);
2010 /* The PTE was already valid, so move to the next one */
2012 PointerPte
= MiPdeToPte(PointerPde
);
2014 /* Is the entire VAD committed? If not, fail */
2015 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2017 /* New loop iteration with our new, on-boundary PTE. */
2022 /* Is the PTE demand zero? */
2023 if (PointerPte
->u
.Long
== 0)
2025 /* Is the entire VAD committed? If not, fail */
2026 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2030 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2031 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
2032 (PointerPte
->u
.Hard
.Valid
== 0) &&
2033 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
2034 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
2036 /* Then part of the range is decommitted, so fail */
2041 /* Move to the next PTE */
2043 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2046 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2052 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2053 IN OUT PVOID
*BaseAddress
,
2054 IN OUT PSIZE_T NumberOfBytesToProtect
,
2055 IN ULONG NewAccessProtection
,
2056 OUT PULONG OldAccessProtection OPTIONAL
)
2058 PMEMORY_AREA MemoryArea
;
2059 PMMSUPPORT AddressSpace
;
2060 ULONG OldAccessProtection_
;
2063 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2064 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2066 AddressSpace
= &Process
->Vm
;
2067 MmLockAddressSpace(AddressSpace
);
2068 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2069 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2071 MmUnlockAddressSpace(AddressSpace
);
2072 return STATUS_UNSUCCESSFUL
;
2075 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2077 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2078 Status
= MmProtectSectionView(AddressSpace
,
2081 *NumberOfBytesToProtect
,
2082 NewAccessProtection
,
2083 OldAccessProtection
);
2085 MmUnlockAddressSpace(AddressSpace
);
2092 MiProtectVirtualMemory(IN PEPROCESS Process
,
2093 IN OUT PVOID
*BaseAddress
,
2094 IN OUT PSIZE_T NumberOfBytesToProtect
,
2095 IN ULONG NewAccessProtection
,
2096 OUT PULONG OldAccessProtection OPTIONAL
)
2098 PMEMORY_AREA MemoryArea
;
2100 PMMSUPPORT AddressSpace
;
2101 ULONG_PTR StartingAddress
, EndingAddress
;
2102 PMMPTE PointerPte
, LastPte
;
2106 ULONG ProtectionMask
, OldProtect
;
2108 NTSTATUS Status
= STATUS_SUCCESS
;
2109 PETHREAD Thread
= PsGetCurrentThread();
2110 TABLE_SEARCH_RESULT Result
;
2112 /* Calculate base address for the VAD */
2113 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2114 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2116 /* Calculate the protection mask and make sure it's valid */
2117 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2118 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2120 DPRINT1("Invalid protection mask\n");
2121 return STATUS_INVALID_PAGE_PROTECTION
;
2124 /* Check for ROS specific memory area */
2125 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2126 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2129 return MiRosProtectVirtualMemory(Process
,
2131 NumberOfBytesToProtect
,
2132 NewAccessProtection
,
2133 OldAccessProtection
);
2136 /* Lock the address space and make sure the process isn't already dead */
2137 AddressSpace
= MmGetCurrentAddressSpace();
2138 MmLockAddressSpace(AddressSpace
);
2139 if (Process
->VmDeleted
)
2141 DPRINT1("Process is dying\n");
2142 Status
= STATUS_PROCESS_IS_TERMINATING
;
2146 /* Get the VAD for this address range, and make sure it exists */
2147 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2148 EndingAddress
>> PAGE_SHIFT
,
2150 (PMMADDRESS_NODE
*)&Vad
);
2151 if (Result
!= TableFoundNode
)
2153 DPRINT("Could not find a VAD for this allocation\n");
2154 Status
= STATUS_CONFLICTING_ADDRESSES
;
2158 /* Make sure the address is within this VAD's boundaries */
2159 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2160 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2162 Status
= STATUS_CONFLICTING_ADDRESSES
;
2166 /* These kinds of VADs are not supported atm */
2167 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2168 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2169 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2171 DPRINT1("Illegal VAD for attempting to set protection\n");
2172 Status
= STATUS_CONFLICTING_ADDRESSES
;
2176 /* Check for a VAD whose protection can't be changed */
2177 if (Vad
->u
.VadFlags
.NoChange
== 1)
2179 DPRINT1("Trying to change protection of a NoChange VAD\n");
2180 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2184 /* Is this section, or private memory? */
2185 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2187 /* Not yet supported */
2188 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2190 DPRINT1("Illegal VAD for attempting to set protection\n");
2191 Status
= STATUS_CONFLICTING_ADDRESSES
;
2195 /* Rotate VADs are not yet supported */
2196 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2198 DPRINT1("Illegal VAD for attempting to set protection\n");
2199 Status
= STATUS_CONFLICTING_ADDRESSES
;
2203 /* Not valid on section files */
2204 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2207 DPRINT1("Invalid protection flags for section\n");
2208 Status
= STATUS_INVALID_PARAMETER_4
;
2212 /* Check if data or page file mapping protection PTE is compatible */
2213 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2216 DPRINT1("Fixme: Not checking for valid protection\n");
2219 /* This is a section, and this is not yet supported */
2220 DPRINT1("Section protection not yet supported\n");
2225 /* Private memory, check protection flags */
2226 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2227 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2229 DPRINT1("Invalid protection flags for private memory\n");
2230 Status
= STATUS_INVALID_PARAMETER_4
;
2234 /* Lock the working set */
2235 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2237 /* Check if all pages in this range are committed */
2238 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2245 DPRINT1("The entire range is not committed\n");
2246 Status
= STATUS_NOT_COMMITTED
;
2247 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2251 /* Compute starting and ending PTE and PDE addresses */
2252 PointerPde
= MiAddressToPde(StartingAddress
);
2253 PointerPte
= MiAddressToPte(StartingAddress
);
2254 LastPte
= MiAddressToPte(EndingAddress
);
2256 /* Make this PDE valid */
2257 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2259 /* Save protection of the first page */
2260 if (PointerPte
->u
.Long
!= 0)
2262 /* Capture the page protection and make the PDE valid */
2263 OldProtect
= MiGetPageProtection(PointerPte
);
2264 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2268 /* Grab the old protection from the VAD itself */
2269 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2272 /* Loop all the PTEs now */
2273 while (PointerPte
<= LastPte
)
2275 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2276 if (MiIsPteOnPdeBoundary(PointerPte
))
2278 PointerPde
= MiPteToPde(PointerPte
);
2279 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2282 /* Capture the PTE and check if it was empty */
2283 PteContents
= *PointerPte
;
2284 if (PteContents
.u
.Long
== 0)
2286 /* This used to be a zero PTE and it no longer is, so we must add a
2287 reference to the pagetable. */
2288 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2291 /* Check what kind of PTE we are dealing with */
2292 if (PteContents
.u
.Hard
.Valid
== 1)
2294 /* Get the PFN entry */
2295 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2297 /* We don't support this yet */
2298 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2300 /* Check if the page should not be accessible at all */
2301 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2302 (NewAccessProtection
& PAGE_GUARD
))
2304 KIRQL OldIrql
= MiAcquirePfnLock();
2306 /* Mark the PTE as transition and change its protection */
2307 PteContents
.u
.Hard
.Valid
= 0;
2308 PteContents
.u
.Soft
.Transition
= 1;
2309 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2310 /* Decrease PFN share count and write the PTE */
2311 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2312 // FIXME: remove the page from the WS
2313 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2315 // FIXME: Should invalidate entry in every CPU TLB
2318 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2320 /* We are done for this PTE */
2321 MiReleasePfnLock(OldIrql
);
2325 /* Write the protection mask and write it with a TLB flush */
2326 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2327 MiFlushTbAndCapture(Vad
,
2336 /* We don't support these cases yet */
2337 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2338 //ASSERT(PteContents.u.Soft.Transition == 0);
2340 /* The PTE is already demand-zero, just update the protection mask */
2341 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2342 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2343 ASSERT(PointerPte
->u
.Long
!= 0);
2346 /* Move to the next PTE */
2350 /* Unlock the working set */
2351 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2354 /* Unlock the address space */
2355 MmUnlockAddressSpace(AddressSpace
);
2357 /* Return parameters and success */
2358 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2359 *BaseAddress
= (PVOID
)StartingAddress
;
2360 *OldAccessProtection
= OldProtect
;
2361 return STATUS_SUCCESS
;
2364 /* Unlock the address space and return the failure code */
2365 MmUnlockAddressSpace(AddressSpace
);
2371 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde
,
2372 IN PEPROCESS TargetProcess
,
2375 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2378 // Sanity checks. The latter is because we only use this function with the
2379 // PFN lock not held, so it may go away in the future.
2381 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2382 ASSERT(OldIrql
== MM_NOIRQL
);
2385 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2386 // return the same address as the PDE on 2-level page table systems.
2388 // If everything is already valid, there is nothing to do.
2390 PointerPpe
= MiAddressToPte(PointerPde
);
2391 PointerPxe
= MiAddressToPde(PointerPde
);
2392 if ((PointerPxe
->u
.Hard
.Valid
) &&
2393 (PointerPpe
->u
.Hard
.Valid
) &&
2394 (PointerPde
->u
.Hard
.Valid
))
2400 // At least something is invalid, so begin by getting the PTE for the PDE itself
2401 // and then lookup each additional level. We must do it in this precise order
2402 // because the pagfault.c code (as well as in Windows) depends that the next
2403 // level up (higher) must be valid when faulting a lower level
2405 PointerPte
= MiPteToAddress(PointerPde
);
2409 // Make sure APCs continued to be disabled
2411 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2414 // First, make the PXE valid if needed
2416 if (!PointerPxe
->u
.Hard
.Valid
)
2418 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2419 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2425 if (!PointerPpe
->u
.Hard
.Valid
)
2427 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2428 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2432 // And finally, make the PDE itself valid.
2434 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2437 // This should've worked the first time so the loop is really just for
2438 // show -- ASSERT that we're actually NOT going to be looping.
2440 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2441 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2442 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2443 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2444 !(PointerPpe
->u
.Hard
.Valid
) ||
2445 !(PointerPde
->u
.Hard
.Valid
));
2450 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2456 PFN_NUMBER PageFrameIndex
;
2460 // Acquire the PFN lock and loop all the PTEs in the list
2462 OldIrql
= MiAcquirePfnLock();
2463 for (i
= 0; i
!= Count
; i
++)
2466 // The PTE must currently be valid
2468 TempPte
= *ValidPteList
[i
];
2469 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2472 // Get the PFN entry for the page itself, and then for its page table
2474 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2475 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2476 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2479 // Decrement the share count on the page table, and then on the page
2482 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2483 MI_SET_PFN_DELETED(Pfn1
);
2484 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2487 // Make the page decommitted
2489 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2493 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2494 // and then release the PFN lock
2497 MiReleasePfnLock(OldIrql
);
2502 MiDecommitPages(IN PVOID StartingAddress
,
2503 IN PMMPTE EndingPte
,
2504 IN PEPROCESS Process
,
2507 PMMPTE PointerPte
, CommitPte
= NULL
;
2509 ULONG CommitReduction
= 0;
2510 PMMPTE ValidPteList
[256];
2514 PETHREAD CurrentThread
= PsGetCurrentThread();
2517 // Get the PTE and PTE for the address, and lock the working set
2518 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2519 // commited range ends so that we can do the right accounting.
2521 PointerPde
= MiAddressToPde(StartingAddress
);
2522 PointerPte
= MiAddressToPte(StartingAddress
);
2523 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2524 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2527 // Make the PDE valid, and now loop through each page's worth of data
2529 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2530 while (PointerPte
<= EndingPte
)
2533 // Check if we've crossed a PDE boundary
2535 if (MiIsPteOnPdeBoundary(PointerPte
))
2538 // Get the new PDE and flush the valid PTEs we had built up until
2539 // now. This helps reduce the amount of TLB flushing we have to do.
2540 // Note that Windows does a much better job using timestamps and
2541 // such, and does not flush the entire TLB all the time, but right
2542 // now we have bigger problems to worry about than TLB flushing.
2544 PointerPde
= MiAddressToPde(StartingAddress
);
2547 MiProcessValidPteList(ValidPteList
, PteCount
);
2552 // Make this PDE valid
2554 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2558 // Read this PTE. It might be active or still demand-zero.
2560 PteContents
= *PointerPte
;
2561 if (PteContents
.u
.Long
)
2564 // The PTE is active. It might be valid and in a working set, or
2565 // it might be a prototype PTE or paged out or even in transition.
2567 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2570 // It's already decommited, so there's nothing for us to do here
2577 // Remove it from the counters, and check if it was valid or not
2579 //Process->NumberOfPrivatePages--;
2580 if (PteContents
.u
.Hard
.Valid
)
2583 // It's valid. At this point make sure that it is not a ROS
2584 // PFN. Also, we don't support ProtoPTEs in this code path.
2586 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2587 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2588 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2591 // Flush any pending PTEs that we had not yet flushed, if our
2592 // list has gotten too big, then add this PTE to the flush list.
2594 if (PteCount
== 256)
2596 MiProcessValidPteList(ValidPteList
, PteCount
);
2599 ValidPteList
[PteCount
++] = PointerPte
;
2604 // We do not support any of these other scenarios at the moment
2606 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2607 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2608 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2611 // So the only other possibility is that it is still a demand
2612 // zero PTE, in which case we undo the accounting we did
2613 // earlier and simply make the page decommitted.
2615 //Process->NumberOfPrivatePages++;
2616 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2623 // This used to be a zero PTE and it no longer is, so we must add a
2624 // reference to the pagetable.
2626 MiIncrementPageTableReferences(StartingAddress
);
2629 // Next, we account for decommitted PTEs and make the PTE as such
2631 if (PointerPte
> CommitPte
) CommitReduction
++;
2632 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2636 // Move to the next PTE and the next address
2639 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2643 // Flush any dangling PTEs from the loop in the last page table, and then
2644 // release the working set and return the commit reduction accounting.
2646 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2647 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2648 return CommitReduction
;
2651 /* PUBLIC FUNCTIONS ***********************************************************/
2658 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2669 MmSecureVirtualMemory(IN PVOID Address
,
2673 static ULONG Warn
; if (!Warn
++) UNIMPLEMENTED
;
2682 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2684 static ULONG Warn
; if (!Warn
++) UNIMPLEMENTED
;
2687 /* SYSTEM CALLS ***************************************************************/
2691 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2692 IN PVOID BaseAddress
,
2694 IN SIZE_T NumberOfBytesToRead
,
2695 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2697 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2699 NTSTATUS Status
= STATUS_SUCCESS
;
2700 SIZE_T BytesRead
= 0;
2704 // Check if we came from user mode
2706 if (PreviousMode
!= KernelMode
)
2709 // Validate the read addresses
2711 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2712 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2713 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2714 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2717 // Don't allow to write into kernel space
2719 return STATUS_ACCESS_VIOLATION
;
2723 // Enter SEH for probe
2728 // Probe the output value
2730 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2732 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2735 // Get exception code
2737 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2743 // Don't do zero-byte transfers
2745 if (NumberOfBytesToRead
)
2748 // Reference the process
2750 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2756 if (NT_SUCCESS(Status
))
2761 Status
= MmCopyVirtualMemory(Process
,
2763 PsGetCurrentProcess(),
2765 NumberOfBytesToRead
,
2770 // Dereference the process
2772 ObDereferenceObject(Process
);
2777 // Check if the caller sent this parameter
2779 if (NumberOfBytesRead
)
2782 // Enter SEH to guard write
2787 // Return the number of bytes read
2789 *NumberOfBytesRead
= BytesRead
;
2791 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2805 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2806 IN PVOID BaseAddress
,
2808 IN SIZE_T NumberOfBytesToWrite
,
2809 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2811 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2813 NTSTATUS Status
= STATUS_SUCCESS
;
2814 SIZE_T BytesWritten
= 0;
2818 // Check if we came from user mode
2820 if (PreviousMode
!= KernelMode
)
2823 // Validate the read addresses
2825 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2826 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2827 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2828 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2831 // Don't allow to write into kernel space
2833 return STATUS_ACCESS_VIOLATION
;
2837 // Enter SEH for probe
2842 // Probe the output value
2844 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2846 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2849 // Get exception code
2851 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2857 // Don't do zero-byte transfers
2859 if (NumberOfBytesToWrite
)
2862 // Reference the process
2864 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2870 if (NT_SUCCESS(Status
))
2875 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2879 NumberOfBytesToWrite
,
2884 // Dereference the process
2886 ObDereferenceObject(Process
);
2891 // Check if the caller sent this parameter
2893 if (NumberOfBytesWritten
)
2896 // Enter SEH to guard write
2901 // Return the number of bytes written
2903 *NumberOfBytesWritten
= BytesWritten
;
2905 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2919 NtFlushInstructionCache(_In_ HANDLE ProcessHandle
,
2920 _In_opt_ PVOID BaseAddress
,
2921 _In_ SIZE_T FlushSize
)
2923 KAPC_STATE ApcState
;
2928 /* Is a base address given? */
2929 if (BaseAddress
!= NULL
)
2931 /* If the requested size is 0, there is nothing to do */
2934 return STATUS_SUCCESS
;
2937 /* Is this a user mode call? */
2938 if (ExGetPreviousMode() != KernelMode
)
2940 /* Make sure the base address is in user space */
2941 if (BaseAddress
> MmHighestUserAddress
)
2943 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress
);
2944 return STATUS_ACCESS_VIOLATION
;
2949 /* Is another process requested? */
2950 if (ProcessHandle
!= NtCurrentProcess())
2952 /* Reference the process */
2953 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2956 ExGetPreviousMode(),
2959 if (!NT_SUCCESS(Status
))
2961 DPRINT1("Failed to reference the process %p\n", ProcessHandle
);
2965 /* Attach to the process */
2966 KeStackAttachProcess(Process
, &ApcState
);
2970 KeSweepICache(BaseAddress
, FlushSize
);
2972 /* Check if we attached */
2973 if (ProcessHandle
!= NtCurrentProcess())
2975 /* Detach from the process and dereference it */
2976 KeUnstackDetachProcess(&ApcState
);
2977 ObDereferenceObject(Process
);
2980 /* All done, return to caller */
2981 return STATUS_SUCCESS
;
2986 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2987 IN OUT PVOID
*UnsafeBaseAddress
,
2988 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2989 IN ULONG NewAccessProtection
,
2990 OUT PULONG UnsafeOldAccessProtection
)
2993 ULONG OldAccessProtection
;
2995 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2996 PVOID BaseAddress
= NULL
;
2997 SIZE_T NumberOfBytesToProtect
= 0;
2998 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3000 BOOLEAN Attached
= FALSE
;
3001 KAPC_STATE ApcState
;
3005 // Check for valid protection flags
3007 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
3008 if (Protection
!= PAGE_NOACCESS
&&
3009 Protection
!= PAGE_READONLY
&&
3010 Protection
!= PAGE_READWRITE
&&
3011 Protection
!= PAGE_WRITECOPY
&&
3012 Protection
!= PAGE_EXECUTE
&&
3013 Protection
!= PAGE_EXECUTE_READ
&&
3014 Protection
!= PAGE_EXECUTE_READWRITE
&&
3015 Protection
!= PAGE_EXECUTE_WRITECOPY
)
3020 return STATUS_INVALID_PAGE_PROTECTION
;
3024 // Check if we came from user mode
3026 if (PreviousMode
!= KernelMode
)
3029 // Enter SEH for probing
3034 // Validate all outputs
3036 ProbeForWritePointer(UnsafeBaseAddress
);
3037 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
3038 ProbeForWriteUlong(UnsafeOldAccessProtection
);
3043 BaseAddress
= *UnsafeBaseAddress
;
3044 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
3046 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3049 // Get exception code
3051 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3060 BaseAddress
= *UnsafeBaseAddress
;
3061 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
3065 // Catch illegal base address
3067 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3070 // Catch illegal region size
3072 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
3077 return STATUS_INVALID_PARAMETER_3
;
3081 // 0 is also illegal
3083 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
3086 // Get a reference to the process
3088 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3089 PROCESS_VM_OPERATION
,
3094 if (!NT_SUCCESS(Status
)) return Status
;
3097 // Check if we should attach
3099 if (CurrentProcess
!= Process
)
3104 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3109 // Do the actual work
3111 Status
= MiProtectVirtualMemory(Process
,
3113 &NumberOfBytesToProtect
,
3114 NewAccessProtection
,
3115 &OldAccessProtection
);
3120 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3123 // Release reference
3125 ObDereferenceObject(Process
);
3128 // Enter SEH to return data
3133 // Return data to user
3135 *UnsafeOldAccessProtection
= OldAccessProtection
;
3136 *UnsafeBaseAddress
= BaseAddress
;
3137 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3139 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3156 // HACK until we have proper WSLIST support
3157 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3159 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3161 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3173 // HACK until we have proper WSLIST support
3174 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3176 if (!Wsle
->u1
.e1
.LockedInWs
&&
3177 !Wsle
->u1
.e1
.LockedInMemory
)
3179 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3182 if (LockType
& MAP_PROCESS
)
3183 Wsle
->u1
.e1
.LockedInWs
= 1;
3184 if (LockType
& MAP_SYSTEM
)
3185 Wsle
->u1
.e1
.LockedInMemory
= 1;
3194 // HACK until we have proper WSLIST support
3195 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3197 if (LockType
& MAP_PROCESS
)
3198 Wsle
->u1
.e1
.LockedInWs
= 0;
3199 if (LockType
& MAP_SYSTEM
)
3200 Wsle
->u1
.e1
.LockedInMemory
= 0;
3202 if (!Wsle
->u1
.e1
.LockedInWs
&&
3203 !Wsle
->u1
.e1
.LockedInMemory
)
3205 MiDereferencePfnAndDropLockCount(Pfn1
);
3211 MiCheckVadsForLockOperation(
3212 _Inout_ PVOID
*BaseAddress
,
3213 _Inout_ PSIZE_T RegionSize
,
3214 _Inout_ PVOID
*EndAddress
)
3220 /* Get the base address and align the start address */
3221 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3222 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3223 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3225 /* First loop and check all VADs */
3226 CurrentVa
= *BaseAddress
;
3227 while (CurrentVa
< *EndAddress
)
3230 Vad
= MiLocateAddress(CurrentVa
);
3233 /// FIXME: this might be a memory area for a section view...
3234 return STATUS_ACCESS_VIOLATION
;
3237 /* Check VAD type */
3238 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3239 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3240 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3242 *EndAddress
= CurrentVa
;
3243 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3244 return STATUS_INCOMPATIBLE_FILE_MAP
;
3247 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3250 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3251 return STATUS_SUCCESS
;
3256 MiLockVirtualMemory(
3257 IN OUT PVOID
*BaseAddress
,
3258 IN OUT PSIZE_T RegionSize
,
3261 PEPROCESS CurrentProcess
;
3262 PMMSUPPORT AddressSpace
;
3263 PVOID CurrentVa
, EndAddress
;
3264 PMMPTE PointerPte
, LastPte
;
3266 #if (_MI_PAGING_LEVELS >= 3)
3269 #if (_MI_PAGING_LEVELS == 4)
3273 NTSTATUS Status
, TempStatus
;
3275 /* Lock the address space */
3276 AddressSpace
= MmGetCurrentAddressSpace();
3277 MmLockAddressSpace(AddressSpace
);
3279 /* Make sure we still have an address space */
3280 CurrentProcess
= PsGetCurrentProcess();
3281 if (CurrentProcess
->VmDeleted
)
3283 Status
= STATUS_PROCESS_IS_TERMINATING
;
3287 /* Check the VADs in the requested range */
3288 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3289 if (!NT_SUCCESS(Status
))
3294 /* Enter SEH for probing */
3297 /* Loop all pages and probe them */
3298 CurrentVa
= *BaseAddress
;
3299 while (CurrentVa
< EndAddress
)
3301 (void)(*(volatile CHAR
*)CurrentVa
);
3302 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3305 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3307 Status
= _SEH2_GetExceptionCode();
3312 /* All pages were accessible, since we hold the address space lock, nothing
3313 can be de-committed. Assume success for now. */
3314 Status
= STATUS_SUCCESS
;
3316 /* Get the PTE and PDE */
3317 PointerPte
= MiAddressToPte(*BaseAddress
);
3318 PointerPde
= MiAddressToPde(*BaseAddress
);
3319 #if (_MI_PAGING_LEVELS >= 3)
3320 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3322 #if (_MI_PAGING_LEVELS == 4)
3323 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3326 /* Get the last PTE */
3327 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3329 /* Lock the process working set */
3330 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3332 /* Loop the pages */
3335 /* Check for a page that is not accessible */
3337 #if (_MI_PAGING_LEVELS == 4)
3338 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3340 #if (_MI_PAGING_LEVELS >= 3)
3341 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3343 (PointerPde
->u
.Hard
.Valid
== 0) ||
3344 (PointerPte
->u
.Hard
.Valid
== 0))
3346 /* Release process working set */
3347 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3349 /* Access the page */
3350 CurrentVa
= MiPteToAddress(PointerPte
);
3352 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3353 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3354 if (!NT_SUCCESS(TempStatus
))
3356 // This should only happen, when remote backing storage is not accessible
3358 Status
= TempStatus
;
3362 /* Lock the process working set */
3363 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3367 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3368 ASSERT(Pfn1
!= NULL
);
3370 /* Check the previous lock status */
3371 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3373 Status
= STATUS_WAS_LOCKED
;
3377 MI_LOCK_VA(Pfn1
, MapType
);
3379 /* Go to the next PTE */
3382 /* Check if we're on a PDE boundary */
3383 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3384 #if (_MI_PAGING_LEVELS >= 3)
3385 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3387 #if (_MI_PAGING_LEVELS == 4)
3388 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3390 } while (PointerPte
<= LastPte
);
3392 /* Release process working set */
3393 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3396 /* Unlock address space */
3397 MmUnlockAddressSpace(AddressSpace
);
3404 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3405 IN OUT PVOID
*BaseAddress
,
3406 IN OUT PSIZE_T NumberOfBytesToLock
,
3410 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3412 BOOLEAN Attached
= FALSE
;
3413 KAPC_STATE ApcState
;
3414 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3415 PVOID CapturedBaseAddress
;
3416 SIZE_T CapturedBytesToLock
;
3422 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3425 // Invalid set of flags
3427 return STATUS_INVALID_PARAMETER
;
3431 // At least one flag must be specified
3433 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3438 return STATUS_INVALID_PARAMETER
;
3442 // Enter SEH for probing
3447 // Validate output data
3449 ProbeForWritePointer(BaseAddress
);
3450 ProbeForWriteSize_t(NumberOfBytesToLock
);
3455 CapturedBaseAddress
= *BaseAddress
;
3456 CapturedBytesToLock
= *NumberOfBytesToLock
;
3458 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3461 // Get exception code
3463 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3468 // Catch illegal base address
3470 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3473 // Catch illegal region size
3475 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3480 return STATUS_INVALID_PARAMETER
;
3484 // 0 is also illegal
3486 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3489 // Get a reference to the process
3491 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3492 PROCESS_VM_OPERATION
,
3497 if (!NT_SUCCESS(Status
)) return Status
;
3500 // Check if this is is system-mapped
3502 if (MapType
& MAP_SYSTEM
)
3505 // Check for required privilege
3507 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3510 // Fail: Don't have it
3512 ObDereferenceObject(Process
);
3513 return STATUS_PRIVILEGE_NOT_HELD
;
3518 // Check if we should attach
3520 if (CurrentProcess
!= Process
)
3525 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3530 // Call the internal function
3532 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3533 &CapturedBytesToLock
,
3539 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3542 // Release reference
3544 ObDereferenceObject(Process
);
3547 // Enter SEH to return data
3552 // Return data to user
3554 *BaseAddress
= CapturedBaseAddress
;
3555 *NumberOfBytesToLock
= CapturedBytesToLock
;
3557 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3560 // Get exception code
3562 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3575 MiUnlockVirtualMemory(
3576 IN OUT PVOID
*BaseAddress
,
3577 IN OUT PSIZE_T RegionSize
,
3580 PEPROCESS CurrentProcess
;
3581 PMMSUPPORT AddressSpace
;
3583 PMMPTE PointerPte
, LastPte
;
3585 #if (_MI_PAGING_LEVELS >= 3)
3588 #if (_MI_PAGING_LEVELS == 4)
3594 /* Lock the address space */
3595 AddressSpace
= MmGetCurrentAddressSpace();
3596 MmLockAddressSpace(AddressSpace
);
3598 /* Make sure we still have an address space */
3599 CurrentProcess
= PsGetCurrentProcess();
3600 if (CurrentProcess
->VmDeleted
)
3602 Status
= STATUS_PROCESS_IS_TERMINATING
;
3606 /* Check the VADs in the requested range */
3607 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3609 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3610 incompatible VAD we continue, like Windows does */
3611 if (Status
== STATUS_ACCESS_VIOLATION
)
3613 Status
= STATUS_NOT_LOCKED
;
3617 /* Get the PTE and PDE */
3618 PointerPte
= MiAddressToPte(*BaseAddress
);
3619 PointerPde
= MiAddressToPde(*BaseAddress
);
3620 #if (_MI_PAGING_LEVELS >= 3)
3621 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3623 #if (_MI_PAGING_LEVELS == 4)
3624 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3627 /* Get the last PTE */
3628 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3630 /* Lock the process working set */
3631 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3633 /* Loop the pages */
3636 /* Check for a page that is not present */
3638 #if (_MI_PAGING_LEVELS == 4)
3639 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3641 #if (_MI_PAGING_LEVELS >= 3)
3642 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3644 (PointerPde
->u
.Hard
.Valid
== 0) ||
3645 (PointerPte
->u
.Hard
.Valid
== 0))
3647 /* Remember it, but keep going */
3648 Status
= STATUS_NOT_LOCKED
;
3653 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3654 ASSERT(Pfn1
!= NULL
);
3656 /* Check if all of the requested locks are present */
3657 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3658 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3660 /* Remember it, but keep going */
3661 Status
= STATUS_NOT_LOCKED
;
3663 /* Check if no lock is present */
3664 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3666 DPRINT1("FIXME: Should remove the page from WS\n");
3671 /* Go to the next PTE */
3674 /* Check if we're on a PDE boundary */
3675 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3676 #if (_MI_PAGING_LEVELS >= 3)
3677 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3679 #if (_MI_PAGING_LEVELS == 4)
3680 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3682 } while (PointerPte
<= LastPte
);
3684 /* Check if we hit a page that was not locked */
3685 if (Status
== STATUS_NOT_LOCKED
)
3687 goto CleanupWithWsLock
;
3690 /* All pages in the region were locked, so unlock them all */
3692 /* Get the PTE and PDE */
3693 PointerPte
= MiAddressToPte(*BaseAddress
);
3694 PointerPde
= MiAddressToPde(*BaseAddress
);
3695 #if (_MI_PAGING_LEVELS >= 3)
3696 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3698 #if (_MI_PAGING_LEVELS == 4)
3699 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3702 /* Loop the pages */
3706 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3707 MI_UNLOCK_VA(Pfn1
, MapType
);
3709 /* Go to the next PTE */
3712 /* Check if we're on a PDE boundary */
3713 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3714 #if (_MI_PAGING_LEVELS >= 3)
3715 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3717 #if (_MI_PAGING_LEVELS == 4)
3718 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3720 } while (PointerPte
<= LastPte
);
3722 /* Everything is done */
3723 Status
= STATUS_SUCCESS
;
3727 /* Release process working set */
3728 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3731 /* Unlock address space */
3732 MmUnlockAddressSpace(AddressSpace
);
3740 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3741 IN OUT PVOID
*BaseAddress
,
3742 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3746 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3748 BOOLEAN Attached
= FALSE
;
3749 KAPC_STATE ApcState
;
3750 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3751 PVOID CapturedBaseAddress
;
3752 SIZE_T CapturedBytesToUnlock
;
3758 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3761 // Invalid set of flags
3763 return STATUS_INVALID_PARAMETER
;
3767 // At least one flag must be specified
3769 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3774 return STATUS_INVALID_PARAMETER
;
3778 // Enter SEH for probing
3783 // Validate output data
3785 ProbeForWritePointer(BaseAddress
);
3786 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3791 CapturedBaseAddress
= *BaseAddress
;
3792 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3797 // Get exception code
3799 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3804 // Catch illegal base address
3806 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3809 // Catch illegal region size
3811 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3816 return STATUS_INVALID_PARAMETER
;
3820 // 0 is also illegal
3822 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3825 // Get a reference to the process
3827 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3828 PROCESS_VM_OPERATION
,
3833 if (!NT_SUCCESS(Status
)) return Status
;
3836 // Check if this is is system-mapped
3838 if (MapType
& MAP_SYSTEM
)
3841 // Check for required privilege
3843 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3846 // Fail: Don't have it
3848 ObDereferenceObject(Process
);
3849 return STATUS_PRIVILEGE_NOT_HELD
;
3854 // Check if we should attach
3856 if (CurrentProcess
!= Process
)
3861 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3866 // Call the internal function
3868 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3869 &CapturedBytesToUnlock
,
3875 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3878 // Release reference
3880 ObDereferenceObject(Process
);
3883 // Enter SEH to return data
3888 // Return data to user
3890 *BaseAddress
= CapturedBaseAddress
;
3891 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3896 // Get exception code
3898 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3905 return STATUS_SUCCESS
;
3910 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3911 IN OUT PVOID
*BaseAddress
,
3912 IN OUT PSIZE_T NumberOfBytesToFlush
,
3913 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3917 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3918 PVOID CapturedBaseAddress
;
3919 SIZE_T CapturedBytesToFlush
;
3920 IO_STATUS_BLOCK LocalStatusBlock
;
3924 // Check if we came from user mode
3926 if (PreviousMode
!= KernelMode
)
3929 // Enter SEH for probing
3934 // Validate all outputs
3936 ProbeForWritePointer(BaseAddress
);
3937 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3938 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3943 CapturedBaseAddress
= *BaseAddress
;
3944 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3946 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3949 // Get exception code
3951 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3960 CapturedBaseAddress
= *BaseAddress
;
3961 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3965 // Catch illegal base address
3967 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3970 // Catch illegal region size
3972 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3977 return STATUS_INVALID_PARAMETER
;
3981 // Get a reference to the process
3983 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3984 PROCESS_VM_OPERATION
,
3989 if (!NT_SUCCESS(Status
)) return Status
;
3994 Status
= MmFlushVirtualMemory(Process
,
3995 &CapturedBaseAddress
,
3996 &CapturedBytesToFlush
,
4000 // Release reference
4002 ObDereferenceObject(Process
);
4005 // Enter SEH to return data
4010 // Return data to user
4012 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
4013 *NumberOfBytesToFlush
= 0;
4014 *IoStatusBlock
= LocalStatusBlock
;
4016 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4032 NtGetWriteWatch(IN HANDLE ProcessHandle
,
4034 IN PVOID BaseAddress
,
4035 IN SIZE_T RegionSize
,
4036 IN PVOID
*UserAddressArray
,
4037 OUT PULONG_PTR EntriesInUserAddressArray
,
4038 OUT PULONG Granularity
)
4043 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4044 ULONG_PTR CapturedEntryCount
;
4048 // Check if we came from user mode
4050 if (PreviousMode
!= KernelMode
)
4053 // Enter SEH for probing
4058 // Catch illegal base address
4060 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
4063 // Catch illegal region size
4065 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4070 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
4074 // Validate all data
4076 ProbeForWriteSize_t(EntriesInUserAddressArray
);
4077 ProbeForWriteUlong(Granularity
);
4082 CapturedEntryCount
= *EntriesInUserAddressArray
;
4085 // Must have a count
4087 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4090 // Can't be larger than the maximum
4092 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
4097 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4101 // Probe the actual array
4103 ProbeForWrite(UserAddressArray
,
4104 CapturedEntryCount
* sizeof(PVOID
),
4107 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4110 // Get exception code
4112 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4121 CapturedEntryCount
= *EntriesInUserAddressArray
;
4122 ASSERT(CapturedEntryCount
!= 0);
4126 // Check if this is a local request
4128 if (ProcessHandle
== NtCurrentProcess())
4131 // No need to reference the process
4133 Process
= PsGetCurrentProcess();
4138 // Reference the target
4140 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4141 PROCESS_VM_OPERATION
,
4146 if (!NT_SUCCESS(Status
)) return Status
;
4150 // Compute the last address and validate it
4152 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4153 if (BaseAddress
> EndAddress
)
4158 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4159 return STATUS_INVALID_PARAMETER_4
;
4168 // Dereference if needed
4170 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4173 // Enter SEH to return data
4178 // Return data to user
4180 *EntriesInUserAddressArray
= 0;
4181 *Granularity
= PAGE_SIZE
;
4183 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4186 // Get exception code
4188 Status
= _SEH2_GetExceptionCode();
4195 return STATUS_SUCCESS
;
4203 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4204 IN PVOID BaseAddress
,
4205 IN SIZE_T RegionSize
)
4210 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4211 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4214 // Catch illegal base address
4216 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4219 // Catch illegal region size
4221 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4226 return STATUS_INVALID_PARAMETER_3
;
4230 // Check if this is a local request
4232 if (ProcessHandle
== NtCurrentProcess())
4235 // No need to reference the process
4237 Process
= PsGetCurrentProcess();
4242 // Reference the target
4244 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4245 PROCESS_VM_OPERATION
,
4250 if (!NT_SUCCESS(Status
)) return Status
;
4254 // Compute the last address and validate it
4256 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4257 if (BaseAddress
> EndAddress
)
4262 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4263 return STATUS_INVALID_PARAMETER_3
;
4272 // Dereference if needed
4274 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4279 return STATUS_SUCCESS
;
4284 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4285 IN PVOID BaseAddress
,
4286 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4287 OUT PVOID MemoryInformation
,
4288 IN SIZE_T MemoryInformationLength
,
4289 OUT PSIZE_T ReturnLength
)
4291 NTSTATUS Status
= STATUS_SUCCESS
;
4292 KPROCESSOR_MODE PreviousMode
;
4294 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4296 /* Bail out if the address is invalid */
4297 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4299 /* Probe return buffer */
4300 PreviousMode
= ExGetPreviousMode();
4301 if (PreviousMode
!= KernelMode
)
4305 ProbeForWrite(MemoryInformation
,
4306 MemoryInformationLength
,
4309 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4311 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4313 Status
= _SEH2_GetExceptionCode();
4317 if (!NT_SUCCESS(Status
))
4323 switch(MemoryInformationClass
)
4325 case MemoryBasicInformation
:
4326 /* Validate the size information of the class */
4327 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4329 /* The size is invalid */
4330 return STATUS_INFO_LENGTH_MISMATCH
;
4332 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4335 MemoryInformationLength
,
4339 case MemorySectionName
:
4340 /* Validate the size information of the class */
4341 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4343 /* The size is invalid */
4344 return STATUS_INFO_LENGTH_MISMATCH
;
4346 Status
= MiQueryMemorySectionName(ProcessHandle
,
4349 MemoryInformationLength
,
4352 case MemoryWorkingSetList
:
4353 case MemoryBasicVlmInformation
:
4355 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4367 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4368 IN OUT PVOID
* UBaseAddress
,
4369 IN ULONG_PTR ZeroBits
,
4370 IN OUT PSIZE_T URegionSize
,
4371 IN ULONG AllocationType
,
4375 PMEMORY_AREA MemoryArea
;
4376 PMMVAD Vad
= NULL
, FoundVad
;
4378 PMMSUPPORT AddressSpace
;
4380 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
4381 ULONG_PTR HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4382 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4383 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4384 PETHREAD CurrentThread
= PsGetCurrentThread();
4385 KAPC_STATE ApcState
;
4386 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4387 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4389 PMMPTE PointerPte
, LastPte
;
4391 TABLE_SEARCH_RESULT Result
;
4394 /* Check for valid Zero bits */
4395 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4397 DPRINT1("Too many zero bits\n");
4398 return STATUS_INVALID_PARAMETER_3
;
4401 /* Check for valid Allocation Types */
4402 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4403 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4405 DPRINT1("Invalid Allocation Type\n");
4406 return STATUS_INVALID_PARAMETER_5
;
4409 /* Check for at least one of these Allocation Types to be set */
4410 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4412 DPRINT1("No memory allocation base type\n");
4413 return STATUS_INVALID_PARAMETER_5
;
4416 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4417 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4419 DPRINT1("Invalid use of MEM_RESET\n");
4420 return STATUS_INVALID_PARAMETER_5
;
4423 /* Check if large pages are being used */
4424 if (AllocationType
& MEM_LARGE_PAGES
)
4426 /* Large page allocations MUST be committed */
4427 if (!(AllocationType
& MEM_COMMIT
))
4429 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4430 return STATUS_INVALID_PARAMETER_5
;
4433 /* These flags are not allowed with large page allocations */
4434 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4436 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4437 return STATUS_INVALID_PARAMETER_5
;
4441 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4442 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4444 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4445 return STATUS_INVALID_PARAMETER_5
;
4448 /* Check for valid MEM_PHYSICAL usage */
4449 if (AllocationType
& MEM_PHYSICAL
)
4451 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4452 if (!(AllocationType
& MEM_RESERVE
))
4454 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4455 return STATUS_INVALID_PARAMETER_5
;
4458 /* Only these flags are allowed with MEM_PHYSIAL */
4459 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4461 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4462 return STATUS_INVALID_PARAMETER_5
;
4465 /* Then make sure PAGE_READWRITE is used */
4466 if (Protect
!= PAGE_READWRITE
)
4468 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4469 return STATUS_INVALID_PARAMETER_6
;
4473 /* Calculate the protection mask and make sure it's valid */
4474 ProtectionMask
= MiMakeProtectionMask(Protect
);
4475 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4477 DPRINT1("Invalid protection mask\n");
4478 return STATUS_INVALID_PAGE_PROTECTION
;
4484 /* Check for user-mode parameters */
4485 if (PreviousMode
!= KernelMode
)
4487 /* Make sure they are writable */
4488 ProbeForWritePointer(UBaseAddress
);
4489 ProbeForWriteSize_t(URegionSize
);
4492 /* Capture their values */
4493 PBaseAddress
= *UBaseAddress
;
4494 PRegionSize
= *URegionSize
;
4496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4498 /* Return the exception code */
4499 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4503 /* Make sure the allocation isn't past the VAD area */
4504 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4506 DPRINT1("Virtual allocation base above User Space\n");
4507 return STATUS_INVALID_PARAMETER_2
;
4510 /* Make sure the allocation wouldn't overflow past the VAD area */
4511 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4513 DPRINT1("Region size would overflow into kernel-memory\n");
4514 return STATUS_INVALID_PARAMETER_4
;
4517 /* Make sure there's a size specified */
4520 DPRINT1("Region size is invalid (zero)\n");
4521 return STATUS_INVALID_PARAMETER_4
;
4525 // If this is for the current process, just use PsGetCurrentProcess
4527 if (ProcessHandle
== NtCurrentProcess())
4529 Process
= CurrentProcess
;
4534 // Otherwise, reference the process with VM rights and attach to it if
4535 // this isn't the current process. We must attach because we'll be touching
4536 // PTEs and PDEs that belong to user-mode memory, and also touching the
4537 // Working Set which is stored in Hyperspace.
4539 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4540 PROCESS_VM_OPERATION
,
4545 if (!NT_SUCCESS(Status
)) return Status
;
4546 if (CurrentProcess
!= Process
)
4548 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4553 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4554 Process
, PBaseAddress
, ZeroBits
, PRegionSize
, AllocationType
, Protect
);
4557 // Check for large page allocations and make sure that the required privilege
4558 // is being held, before attempting to handle them.
4560 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4561 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4563 /* Fail without it */
4564 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4565 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4566 goto FailPathNoLock
;
4570 // Fail on the things we don't yet support
4572 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4574 DPRINT1("MEM_LARGE_PAGES not supported\n");
4575 Status
= STATUS_INVALID_PARAMETER
;
4576 goto FailPathNoLock
;
4578 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4580 DPRINT1("MEM_PHYSICAL not supported\n");
4581 Status
= STATUS_INVALID_PARAMETER
;
4582 goto FailPathNoLock
;
4584 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4586 DPRINT1("MEM_WRITE_WATCH not supported\n");
4587 Status
= STATUS_INVALID_PARAMETER
;
4588 goto FailPathNoLock
;
4592 // Check if the caller is reserving memory, or committing memory and letting
4593 // us pick the base address
4595 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4598 // Do not allow COPY_ON_WRITE through this API
4600 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4602 DPRINT1("Copy on write not allowed through this path\n");
4603 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4604 goto FailPathNoLock
;
4608 // Does the caller have an address in mind, or is this a blind commit?
4613 // This is a blind commit, all we need is the region size
4615 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4617 StartingAddress
= 0;
4620 // Check if ZeroBits were specified
4625 // Calculate the highest address and check if it's valid
4627 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4628 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4630 Status
= STATUS_INVALID_PARAMETER_3
;
4631 goto FailPathNoLock
;
4638 // This is a reservation, so compute the starting address on the
4639 // expected 64KB granularity, and see where the ending address will
4640 // fall based on the aligned address and the passed in region size
4642 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4643 PRegionSize
= EndingAddress
+ 1 - ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4644 StartingAddress
= (ULONG_PTR
)PBaseAddress
;
4648 // Allocate and initialize the VAD
4650 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4653 DPRINT1("Failed to allocate a VAD!\n");
4654 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4655 goto FailPathNoLock
;
4658 RtlZeroMemory(Vad
, sizeof(MMVAD_LONG
));
4659 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4660 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4661 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4662 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4667 Status
= MiInsertVadEx(Vad
,
4671 MM_VIRTMEM_GRANULARITY
,
4673 if (!NT_SUCCESS(Status
))
4675 DPRINT1("Failed to insert the VAD!\n");
4676 goto FailPathNoLock
;
4680 // Detach and dereference the target process if
4681 // it was different from the current process
4683 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4684 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4687 // Use SEH to write back the base address and the region size. In the case
4688 // of an exception, we do not return back the exception code, as the memory
4689 // *has* been allocated. The caller would now have to call VirtualQuery
4690 // or do some other similar trick to actually find out where its memory
4691 // allocation ended up
4695 *URegionSize
= PRegionSize
;
4696 *UBaseAddress
= (PVOID
)StartingAddress
;
4698 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4701 // Ignore exception!
4705 DPRINT("Reserved %x bytes at %p.\n", PRegionSize
, StartingAddress
);
4706 return STATUS_SUCCESS
;
4710 // This is a MEM_COMMIT on top of an existing address which must have been
4711 // MEM_RESERVED already. Compute the start and ending base addresses based
4712 // on the user input, and then compute the actual region size once all the
4713 // alignments have been done.
4715 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4716 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4717 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4720 // Lock the address space and make sure the process isn't already dead
4722 AddressSpace
= MmGetCurrentAddressSpace();
4723 MmLockAddressSpace(AddressSpace
);
4724 if (Process
->VmDeleted
)
4726 DPRINT1("Process is dying\n");
4727 Status
= STATUS_PROCESS_IS_TERMINATING
;
4732 // Get the VAD for this address range, and make sure it exists
4734 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4735 EndingAddress
>> PAGE_SHIFT
,
4737 (PMMADDRESS_NODE
*)&FoundVad
);
4738 if (Result
!= TableFoundNode
)
4740 DPRINT1("Could not find a VAD for this allocation\n");
4741 Status
= STATUS_CONFLICTING_ADDRESSES
;
4745 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4747 /// @todo HACK: pretend success
4748 DPRINT("MEM_RESET not supported\n");
4749 Status
= STATUS_SUCCESS
;
4754 // These kinds of VADs are illegal for this Windows function when trying to
4755 // commit an existing range
4757 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4758 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4759 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4761 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4762 Status
= STATUS_CONFLICTING_ADDRESSES
;
4767 // Make sure that this address range actually fits within the VAD for it
4769 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4770 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4772 DPRINT1("Address range does not fit into the VAD\n");
4773 Status
= STATUS_CONFLICTING_ADDRESSES
;
4778 // Make sure this is an ARM3 section
4780 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4781 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4783 DPRINT1("Illegal commit of non-ARM3 section!\n");
4784 Status
= STATUS_ALREADY_COMMITTED
;
4788 // Is this a previously reserved section being committed? If so, enter the
4789 // special section path
4791 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4794 // You cannot commit large page sections through this API
4796 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4798 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4799 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4804 // You can only use caching flags on a rotate VAD
4806 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4807 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4809 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4810 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4815 // We should make sure that the section's permissions aren't being
4818 if (FoundVad
->u
.VadFlags
.NoChange
)
4821 // Make sure it's okay to touch it
4822 // Note: The Windows 2003 kernel has a bug here, passing the
4823 // unaligned base address together with the aligned size,
4824 // potentially covering a region larger than the actual allocation.
4825 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4826 // For now we keep this behavior.
4827 // TODO: analyze possible implications, create test case
4829 Status
= MiCheckSecuredVad(FoundVad
,
4833 if (!NT_SUCCESS(Status
))
4835 DPRINT1("Secured VAD being messed around with\n");
4841 // ARM3 does not support file-backed sections, only shared memory
4843 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4846 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4848 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4849 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4851 DPRINT1("Invalid page protection for rotate VAD\n");
4852 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4857 // Compute PTE addresses and the quota charge, then grab the commit lock
4859 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4860 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4861 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4862 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4865 // Get the segment template PTE and start looping each page
4867 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4868 ASSERT(TempPte
.u
.Long
!= 0);
4869 while (PointerPte
<= LastPte
)
4872 // For each non-already-committed page, write the invalid template PTE
4874 if (PointerPte
->u
.Long
== 0)
4876 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4886 // Now do the commit accounting and release the lock
4888 ASSERT(QuotaCharge
>= QuotaFree
);
4889 QuotaCharge
-= QuotaFree
;
4890 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4891 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4894 // We are done with committing the section pages
4896 Status
= STATUS_SUCCESS
;
4901 // This is a specific ReactOS check because we only use normal VADs
4903 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4906 // While this is an actual Windows check
4908 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4911 // Throw out attempts to use copy-on-write through this API path
4913 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4915 DPRINT1("Write copy attempted when not allowed\n");
4916 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4921 // Initialize a demand-zero PTE
4924 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4925 ASSERT(TempPte
.u
.Long
!= 0);
4928 // Get the PTE, PDE and the last PTE for this address range
4930 PointerPde
= MiAddressToPde(StartingAddress
);
4931 PointerPte
= MiAddressToPte(StartingAddress
);
4932 LastPte
= MiAddressToPte(EndingAddress
);
4935 // Update the commit charge in the VAD as well as in the process, and check
4936 // if this commit charge was now higher than the last recorded peak, in which
4937 // case we also update the peak
4939 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4940 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4941 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4943 Process
->CommitChargePeak
= Process
->CommitCharge
;
4947 // Lock the working set while we play with user pages and page tables
4949 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4952 // Make the current page table valid, and then loop each page within it
4954 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4955 while (PointerPte
<= LastPte
)
4958 // Have we crossed into a new page table?
4960 if (MiIsPteOnPdeBoundary(PointerPte
))
4963 // Get the PDE and now make it valid too
4965 PointerPde
= MiPteToPde(PointerPte
);
4966 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4970 // Is this a zero PTE as expected?
4972 if (PointerPte
->u
.Long
== 0)
4975 // First increment the count of pages in the page table for this
4978 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4981 // And now write the invalid demand-zero PTE as requested
4983 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4985 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4988 // If the PTE was already decommitted, there is nothing else to do
4989 // but to write the new demand-zero PTE
4991 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4993 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4996 // We don't handle these scenarios yet
4998 if (PointerPte
->u
.Soft
.Valid
== 0)
5000 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
5001 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
5005 // There's a change in protection, remember this for later, but do
5006 // not yet handle it.
5008 ChangeProtection
= TRUE
;
5012 // Move to the next PTE
5018 // Release the working set lock, unlock the address space, and detach from
5019 // the target process if it was not the current process. Also dereference the
5020 // target process if this wasn't the case.
5022 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5023 Status
= STATUS_SUCCESS
;
5025 MmUnlockAddressSpace(AddressSpace
);
5027 if (!NT_SUCCESS(Status
))
5031 ExFreePoolWithTag(Vad
, 'SdaV');
5036 // Check if we need to update the protection
5038 if (ChangeProtection
)
5040 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
5041 SIZE_T ProtectSize
= PRegionSize
;
5042 ULONG OldProtection
;
5045 // Change the protection of the region
5047 MiProtectVirtualMemory(Process
,
5048 &ProtectBaseAddress
,
5055 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5056 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5059 // Only write back results on success
5061 if (NT_SUCCESS(Status
))
5064 // Use SEH to write back the base address and the region size. In the case
5065 // of an exception, we strangely do return back the exception code, even
5066 // though the memory *has* been allocated. This mimics Windows behavior and
5067 // there is not much we can do about it.
5071 *URegionSize
= PRegionSize
;
5072 *UBaseAddress
= (PVOID
)StartingAddress
;
5074 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5076 Status
= _SEH2_GetExceptionCode();
5089 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5090 IN PVOID
* UBaseAddress
,
5091 IN PSIZE_T URegionSize
,
5094 PMEMORY_AREA MemoryArea
;
5097 LONG_PTR AlreadyDecommitted
, CommitReduction
= 0;
5098 ULONG_PTR StartingAddress
, EndingAddress
;
5102 PMMSUPPORT AddressSpace
;
5103 PETHREAD CurrentThread
= PsGetCurrentThread();
5104 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5105 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5106 KAPC_STATE ApcState
;
5107 BOOLEAN Attached
= FALSE
;
5111 // Only two flags are supported
5113 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5115 DPRINT1("Invalid FreeType\n");
5116 return STATUS_INVALID_PARAMETER_4
;
5120 // Check if no flag was used, or if both flags were used
5122 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5123 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5125 DPRINT1("Invalid FreeType combination\n");
5126 return STATUS_INVALID_PARAMETER_4
;
5130 // Enter SEH for probe and capture. On failure, return back to the caller
5131 // with an exception violation.
5136 // Check for user-mode parameters and make sure that they are writeable
5138 if (PreviousMode
!= KernelMode
)
5140 ProbeForWritePointer(UBaseAddress
);
5141 ProbeForWriteUlong(URegionSize
);
5145 // Capture the current values
5147 PBaseAddress
= *UBaseAddress
;
5148 PRegionSize
= *URegionSize
;
5150 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5152 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5157 // Make sure the allocation isn't past the user area
5159 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5161 DPRINT1("Virtual free base above User Space\n");
5162 return STATUS_INVALID_PARAMETER_2
;
5166 // Make sure the allocation wouldn't overflow past the user area
5168 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5170 DPRINT1("Region size would overflow into kernel-memory\n");
5171 return STATUS_INVALID_PARAMETER_3
;
5175 // If this is for the current process, just use PsGetCurrentProcess
5177 if (ProcessHandle
== NtCurrentProcess())
5179 Process
= CurrentProcess
;
5184 // Otherwise, reference the process with VM rights and attach to it if
5185 // this isn't the current process. We must attach because we'll be touching
5186 // PTEs and PDEs that belong to user-mode memory, and also touching the
5187 // Working Set which is stored in Hyperspace.
5189 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5190 PROCESS_VM_OPERATION
,
5195 if (!NT_SUCCESS(Status
)) return Status
;
5196 if (CurrentProcess
!= Process
)
5198 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5203 DPRINT("NtFreeVirtualMemory: Process 0x%p, Adress 0x%p, size 0x%x, FreeType %x.\n",
5204 Process
, PBaseAddress
, PRegionSize
, FreeType
);
5207 // Lock the address space
5209 AddressSpace
= MmGetCurrentAddressSpace();
5210 MmLockAddressSpace(AddressSpace
);
5213 // If the address space is being deleted, fail the de-allocation since it's
5214 // too late to do anything about it
5216 if (Process
->VmDeleted
)
5218 DPRINT1("Process is dead\n");
5219 Status
= STATUS_PROCESS_IS_TERMINATING
;
5224 // Compute start and end addresses, and locate the VAD
5226 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5227 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5228 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5231 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5232 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5237 // If the range exceeds the VAD's ending VPN, fail this request
5239 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5241 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5242 Status
= STATUS_UNABLE_TO_FREE_VM
;
5247 // Only private memory (except rotate VADs) can be freed through here */
5249 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5250 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5251 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5253 DPRINT1("Attempt to free section memory\n");
5254 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5259 // ARM3 does not yet handle protected VM
5261 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5264 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5265 // and that is is an ARM3 memory area, and not a section view, as we currently
5266 // don't support freeing those though this interface.
5268 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5270 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5273 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5275 if (FreeType
& MEM_RELEASE
)
5278 // ARM3 only supports this VAD in this path
5280 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5283 // Is the caller trying to remove the whole VAD, or remove only a portion
5284 // of it? If no region size is specified, then the assumption is that the
5285 // whole VAD is to be destroyed
5290 // The caller must specify the base address identically to the range
5291 // that is stored in the VAD.
5293 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5295 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5296 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5301 // Now compute the actual start/end addresses based on the VAD
5303 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5304 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5307 // Finally lock the working set and remove the VAD from the VAD tree
5309 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5310 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5311 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5316 // This means the caller wants to release a specific region within
5317 // the range. We have to find out which range this is -- the following
5318 // possibilities exist plus their union (CASE D):
5320 // STARTING ADDRESS ENDING ADDRESS
5321 // [<========][========================================][=========>]
5322 // CASE A CASE B CASE C
5325 // First, check for case A or D
5327 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5332 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5335 // This is the easiest one to handle -- it is identical to
5336 // the code path above when the caller sets a zero region size
5337 // and the whole VAD is destroyed
5339 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5340 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5341 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5346 // This case is pretty easy too -- we compute a bunch of
5347 // pages to decommit, and then push the VAD's starting address
5348 // a bit further down, then decrement the commit charge
5350 // NOT YET IMPLEMENTED IN ARM3.
5352 DPRINT1("Case A not handled\n");
5353 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5357 // After analyzing the VAD, set it to NULL so that we don't
5358 // free it in the exit path
5366 // This is case B or case C. First check for case C
5368 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5370 PMEMORY_AREA MemoryArea
;
5373 // This is pretty easy and similar to case A. We compute the
5374 // amount of pages to decommit, update the VAD's commit charge
5375 // and then change the ending address of the VAD to be a bit
5378 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5379 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5383 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5384 // For ReactOS: shrink the corresponding memory area
5385 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5386 ASSERT(Vad
->StartingVpn
== MemoryArea
->VadNode
.StartingVpn
);
5387 ASSERT(Vad
->EndingVpn
== MemoryArea
->VadNode
.EndingVpn
);
5388 Vad
->EndingVpn
= (StartingAddress
- 1) >> PAGE_SHIFT
;
5389 MemoryArea
->VadNode
.EndingVpn
= Vad
->EndingVpn
;
5394 // This is case B and the hardest one. Because we are removing
5395 // a chunk of memory from the very middle of the VAD, we must
5396 // actually split the VAD into two new VADs and compute the
5397 // commit charges for each of them, and reinsert new charges.
5399 // NOT YET IMPLEMENTED IN ARM3.
5401 DPRINT1("Case B not handled\n");
5402 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5407 // After analyzing the VAD, set it to NULL so that we don't
5408 // free it in the exit path
5415 // Now we have a range of pages to dereference, so call the right API
5416 // to do that and then release the working set, since we're done messing
5417 // around with process pages.
5419 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5420 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5421 Status
= STATUS_SUCCESS
;
5425 // Update the process counters
5427 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5428 Process
->CommitCharge
-= CommitReduction
;
5429 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5432 // Unlock the address space and free the VAD in failure cases. Next,
5433 // detach from the target process so we can write the region size and the
5434 // base address to the correct source process, and dereference the target
5437 MmUnlockAddressSpace(AddressSpace
);
5438 if (Vad
) ExFreePool(Vad
);
5439 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5440 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5443 // Use SEH to safely return the region size and the base address of the
5444 // deallocation. If we get an access violation, don't return a failure code
5445 // as the deallocation *has* happened. The caller will just have to figure
5446 // out another way to find out where it is (such as VirtualQuery).
5450 *URegionSize
= PRegionSize
;
5451 *UBaseAddress
= (PVOID
)StartingAddress
;
5453 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5461 // This is the decommit path. You cannot decommit from the following VADs in
5462 // Windows, so fail the vall
5464 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5465 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5466 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5468 DPRINT1("Trying to decommit from invalid VAD\n");
5469 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5474 // If the caller did not specify a region size, first make sure that this
5475 // region is actually committed. If it is, then compute the ending address
5476 // based on the VAD.
5480 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5482 DPRINT1("Decomitting non-committed memory\n");
5483 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5486 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5490 // Decommit the PTEs for the range plus the actual backing pages for the
5491 // range, then reduce that amount from the commit charge in the VAD
5493 AlreadyDecommitted
= MiDecommitPages((PVOID
)StartingAddress
,
5494 MiAddressToPte(EndingAddress
),
5497 CommitReduction
= MiAddressToPte(EndingAddress
) -
5498 MiAddressToPte(StartingAddress
) +
5502 ASSERT(CommitReduction
>= 0);
5503 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5504 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5507 // We are done, go to the exit path without freeing the VAD as it remains
5508 // valid since we have not released the allocation.
5511 Status
= STATUS_SUCCESS
;
5515 // In the failure path, we detach and derefernece the target process, and
5516 // return whatever failure code was sent.
5519 MmUnlockAddressSpace(AddressSpace
);
5520 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5521 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5528 MmGetPhysicalAddress(PVOID Address
)
5530 PHYSICAL_ADDRESS PhysicalAddress
;
5534 /* Check if the PXE/PPE/PDE is valid */
5536 #if (_MI_PAGING_LEVELS == 4)
5537 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5539 #if (_MI_PAGING_LEVELS >= 3)
5540 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5542 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5544 /* Check for large pages */
5545 TempPde
= *MiAddressToPde(Address
);
5546 if (TempPde
.u
.Hard
.LargePage
)
5548 /* Physical address is base page + large page offset */
5549 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5550 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5551 return PhysicalAddress
;
5554 /* Check if the PTE is valid */
5555 TempPte
= *MiAddressToPte(Address
);
5556 if (TempPte
.u
.Hard
.Valid
)
5558 /* Physical address is base page + page offset */
5559 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5560 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5561 return PhysicalAddress
;
5565 KeRosDumpStackFrames(NULL
, 20);
5566 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5567 PhysicalAddress
.QuadPart
= 0;
5568 return PhysicalAddress
;