2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
10 /* So long, and Thanks for All the Fish */
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
32 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
34 IN ULONG ProtectionMask
,
36 IN BOOLEAN CaptureDirtyBit
);
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
44 IN ULONG_PTR EndingAddress
,
48 PMMPTE PointerPte
, LastPte
;
52 /* Compute starting and ending PTE and PDE addresses */
53 PointerPde
= MiAddressToPde(StartingAddress
);
54 PointerPte
= MiAddressToPte(StartingAddress
);
55 LastPte
= MiAddressToPte(EndingAddress
);
57 /* Handle commited pages first */
58 if (Vad
->u
.VadFlags
.MemCommit
== 1)
60 /* This is a committed VAD, so Assume the whole range is committed */
61 CommittedPages
= (ULONG
)BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
63 /* Is the PDE demand-zero? */
64 PointerPde
= MiPteToPde(PointerPte
);
65 if (PointerPde
->u
.Long
!= 0)
67 /* It is not. Is it valid? */
68 if (PointerPde
->u
.Hard
.Valid
== 0)
71 PointerPte
= MiPteToAddress(PointerPde
);
72 MiMakeSystemAddressValid(PointerPte
, Process
);
77 /* It is, skip it and move to the next PDE, unless we're done */
79 PointerPte
= MiPteToAddress(PointerPde
);
80 if (PointerPte
> LastPte
) return CommittedPages
;
83 /* Now loop all the PTEs in the range */
84 while (PointerPte
<= LastPte
)
86 /* Have we crossed a PDE boundary? */
87 if (MiIsPteOnPdeBoundary(PointerPte
))
89 /* Is this PDE demand zero? */
90 PointerPde
= MiPteToPde(PointerPte
);
91 if (PointerPde
->u
.Long
!= 0)
93 /* It isn't -- is it valid? */
94 if (PointerPde
->u
.Hard
.Valid
== 0)
96 /* Nope, fault it in */
97 PointerPte
= MiPteToAddress(PointerPde
);
98 MiMakeSystemAddressValid(PointerPte
, Process
);
103 /* It is, skip it and move to the next PDE */
105 PointerPte
= MiPteToAddress(PointerPde
);
110 /* Is this PTE demand zero? */
111 if (PointerPte
->u
.Long
!= 0)
113 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
114 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
115 (PointerPte
->u
.Hard
.Valid
== 0) &&
116 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
117 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
119 /* It is, so remove it from the count of commited pages */
124 /* Move to the next PTE */
128 /* Return how many committed pages there still are */
129 return CommittedPages
;
132 /* This is a non-commited VAD, so assume none of it is committed */
135 /* Is the PDE demand-zero? */
136 PointerPde
= MiPteToPde(PointerPte
);
137 if (PointerPde
->u
.Long
!= 0)
139 /* It isn't -- is it invalid? */
140 if (PointerPde
->u
.Hard
.Valid
== 0)
142 /* It is, so page it in */
143 PointerPte
= MiPteToAddress(PointerPde
);
144 MiMakeSystemAddressValid(PointerPte
, Process
);
149 /* It is, so skip it and move to the next PDE */
151 PointerPte
= MiPteToAddress(PointerPde
);
152 if (PointerPte
> LastPte
) return CommittedPages
;
155 /* Loop all the PTEs in this PDE */
156 while (PointerPte
<= LastPte
)
158 /* Have we crossed a PDE boundary? */
159 if (MiIsPteOnPdeBoundary(PointerPte
))
161 /* Is this new PDE demand-zero? */
162 PointerPde
= MiPteToPde(PointerPte
);
163 if (PointerPde
->u
.Long
!= 0)
165 /* It isn't. Is it valid? */
166 if (PointerPde
->u
.Hard
.Valid
== 0)
168 /* It isn't, so make it valid */
169 PointerPte
= MiPteToAddress(PointerPde
);
170 MiMakeSystemAddressValid(PointerPte
, Process
);
175 /* It is, so skip it and move to the next one */
177 PointerPte
= MiPteToAddress(PointerPde
);
182 /* Is this PTE demand-zero? */
183 if (PointerPte
->u
.Long
!= 0)
185 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
186 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
187 (PointerPte
->u
.Hard
.Valid
== 1) ||
188 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
189 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
191 /* It is! So we'll treat this as a committed page */
196 /* Move to the next PTE */
200 /* Return how many committed pages we found in this VAD */
201 return CommittedPages
;
206 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
207 IN PEPROCESS CurrentProcess
)
210 BOOLEAN WsShared
= FALSE
, WsSafe
= FALSE
, LockChange
= FALSE
;
211 PETHREAD CurrentThread
= PsGetCurrentThread();
213 /* Must be a non-pool page table, since those are double-mapped already */
214 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
215 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
216 (PageTableVirtualAddress
> MmPagedPoolEnd
));
218 /* Working set lock or PFN lock should be held */
219 ASSERT(KeAreAllApcsDisabled() == TRUE
);
221 /* Check if the page table is valid */
222 while (!MmIsAddressValid(PageTableVirtualAddress
))
224 /* Release the working set lock */
225 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
231 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
232 if (!NT_SUCCESS(Status
))
234 /* This should not fail */
235 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
238 (ULONG_PTR
)CurrentProcess
,
239 (ULONG_PTR
)PageTableVirtualAddress
);
242 /* Lock the working set again */
243 MiLockProcessWorkingSetForFault(CurrentProcess
,
248 /* This flag will be useful later when we do better locking */
252 /* Let caller know what the lock state is */
258 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
262 BOOLEAN LockChange
= FALSE
;
264 /* Must be e kernel address */
265 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
267 /* Check if the page is valid */
268 while (!MmIsAddressValid(VirtualAddress
))
270 /* Release the PFN database */
271 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
274 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
275 if (!NT_SUCCESS(Status
))
277 /* This should not fail */
278 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
282 (ULONG_PTR
)VirtualAddress
);
285 /* This flag will be useful later when we do better locking */
288 /* Lock the PFN database */
289 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
292 /* Let caller know what the lock state is */
298 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
299 IN PFN_NUMBER PageCount
,
301 OUT PPFN_NUMBER ValidPages
)
303 PFN_COUNT ActualPages
= 0;
304 PETHREAD CurrentThread
= PsGetCurrentThread();
306 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
308 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
310 /* Lock the system working set */
311 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
316 /* Make sure there's some data about the page */
317 if (PointerPte
->u
.Long
)
319 /* As always, only handle current ARM3 scenarios */
320 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
321 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
323 /* Normally this is one possibility -- freeing a valid page */
324 if (PointerPte
->u
.Hard
.Valid
)
326 /* Get the page PFN */
327 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
328 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
330 /* Should not have any working set data yet */
331 ASSERT(Pfn1
->u1
.WsIndex
== 0);
333 /* Actual valid, legitimate, pages */
334 if (ValidPages
) (*ValidPages
)++;
336 /* Get the page table entry */
337 PageTableIndex
= Pfn1
->u4
.PteFrame
;
338 Pfn2
= MiGetPfnEntry(PageTableIndex
);
340 /* Lock the PFN database */
341 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
343 /* Delete it the page */
344 MI_SET_PFN_DELETED(Pfn1
);
345 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
347 /* Decrement the page table too */
348 MiDecrementShareCount(Pfn2
, PageTableIndex
);
350 /* Release the PFN database */
351 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
353 /* Destroy the PTE */
354 MI_ERASE_PTE(PointerPte
);
359 * The only other ARM3 possibility is a demand zero page, which would
360 * mean freeing some of the paged pool pages that haven't even been
361 * touched yet, as part of a larger allocation.
363 * Right now, we shouldn't expect any page file information in the PTE
365 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
367 /* Destroy the PTE */
368 MI_ERASE_PTE(PointerPte
);
371 /* Actual legitimate pages */
380 /* Release the working set */
381 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
383 /* Flush the entire TLB */
384 KeFlushEntireTb(TRUE
, TRUE
);
392 MiDeletePte(IN PMMPTE PointerPte
,
393 IN PVOID VirtualAddress
,
394 IN PEPROCESS CurrentProcess
,
395 IN PMMPTE PrototypePte
)
399 PFN_NUMBER PageFrameIndex
;
402 /* PFN lock must be held */
403 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
405 /* Capture the PTE */
406 TempPte
= *PointerPte
;
408 /* See if the PTE is valid */
409 if (TempPte
.u
.Hard
.Valid
== 0)
411 /* Prototype and paged out PTEs not supported yet */
412 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
413 ASSERT((TempPte
.u
.Soft
.PageFileHigh
== 0) || (TempPte
.u
.Soft
.Transition
== 1));
415 if (TempPte
.u
.Soft
.Transition
)
417 /* Get the PFN entry */
418 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
419 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
421 DPRINT("Pte %p is transitional!\n", PointerPte
);
423 /* Destroy the PTE */
424 MI_ERASE_PTE(PointerPte
);
426 /* Drop the reference on the page table. */
427 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
429 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
431 /* Make the page free. For prototypes, it will be made free when deleting the section object */
432 if (Pfn1
->u2
.ShareCount
== 0)
434 NT_ASSERT(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 temporarily mark it as active */
440 MiUnlinkPageFromList(Pfn1
);
441 Pfn1
->u3
.e2
.ReferenceCount
++;
442 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
444 /* This will put it back in free list and clean properly up */
445 MI_SET_PFN_DELETED(Pfn1
);
446 MiDecrementReferenceCount(Pfn1
, PageFrameIndex
);
452 /* Get the PFN entry */
453 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
454 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
456 /* Check if this is a valid, prototype PTE */
457 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
459 /* Get the PDE and make sure it's faulted in */
460 PointerPde
= MiPteToPde(PointerPte
);
461 if (PointerPde
->u
.Hard
.Valid
== 0)
463 #if (_MI_PAGING_LEVELS == 2)
464 /* Could be paged pool access from a new process -- synchronize the page directories */
465 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
468 /* The PDE must be valid at this point */
469 KeBugCheckEx(MEMORY_MANAGEMENT
,
471 (ULONG_PTR
)PointerPte
,
473 (ULONG_PTR
)VirtualAddress
);
475 #if (_MI_PAGING_LEVELS == 2)
478 /* Drop the share count on the page table */
479 PointerPde
= MiPteToPde(PointerPte
);
480 MiDecrementShareCount(MiGetPfnEntry(PointerPde
->u
.Hard
.PageFrameNumber
),
481 PointerPde
->u
.Hard
.PageFrameNumber
);
483 /* Drop the share count */
484 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
486 /* Either a fork, or this is the shared user data page */
487 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
489 /* If it's not the shared user page, then crash, since there's no fork() yet */
490 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
491 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
493 /* Must be some sort of memory corruption */
494 KeBugCheckEx(MEMORY_MANAGEMENT
,
496 (ULONG_PTR
)PointerPte
,
497 (ULONG_PTR
)PrototypePte
,
498 (ULONG_PTR
)Pfn1
->PteAddress
);
503 MI_ERASE_PTE(PointerPte
);
507 /* Make sure the saved PTE address is valid */
508 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
510 /* The PFN entry is illegal, or invalid */
511 KeBugCheckEx(MEMORY_MANAGEMENT
,
513 (ULONG_PTR
)PointerPte
,
515 (ULONG_PTR
)Pfn1
->PteAddress
);
519 MI_ERASE_PTE(PointerPte
);
521 /* There should only be 1 shared reference count */
522 ASSERT(Pfn1
->u2
.ShareCount
== 1);
524 /* Drop the reference on the page table. */
525 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
527 /* Mark the PFN for deletion and dereference what should be the last ref */
528 MI_SET_PFN_DELETED(Pfn1
);
529 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
531 /* We should eventually do this */
532 //CurrentProcess->NumberOfPrivatePages--;
541 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
542 IN ULONG_PTR EndingAddress
,
545 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
548 PEPROCESS CurrentProcess
;
550 BOOLEAN AddressGap
= FALSE
;
551 PSUBSECTION Subsection
;
553 /* Get out if this is a fake VAD, RosMm will free the marea pages */
554 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
556 /* Grab the process and PTE/PDE for the address being deleted */
557 CurrentProcess
= PsGetCurrentProcess();
558 PointerPde
= MiAddressToPde(Va
);
559 PointerPte
= MiAddressToPte(Va
);
561 /* Check if this is a section VAD or a VM VAD */
562 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
564 /* Don't worry about prototypes */
565 PrototypePte
= LastPrototypePte
= NULL
;
569 /* Get the prototype PTE */
570 PrototypePte
= Vad
->FirstPrototypePte
;
571 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
574 /* In all cases, we don't support fork() yet */
575 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
577 /* Loop the PTE for each VA */
580 /* First keep going until we find a valid PDE */
581 while (!PointerPde
->u
.Long
)
583 /* There are gaps in the address space */
586 /* Still no valid PDE, try the next 4MB (or whatever) */
589 /* Update the PTE on this new boundary */
590 PointerPte
= MiPteToAddress(PointerPde
);
592 /* Check if all the PDEs are invalid, so there's nothing to free */
593 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
594 if (Va
> EndingAddress
) return;
597 /* Now check if the PDE is mapped in */
598 if (!PointerPde
->u
.Hard
.Valid
)
600 /* It isn't, so map it in */
601 PointerPte
= MiPteToAddress(PointerPde
);
602 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
605 /* Now we should have a valid PDE, mapped in, and still have some VA */
606 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
607 ASSERT(Va
<= EndingAddress
);
609 /* Check if this is a section VAD with gaps in it */
610 if ((AddressGap
) && (LastPrototypePte
))
612 /* We need to skip to the next correct prototype PTE */
613 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
615 /* And we need the subsection to skip to the next last prototype PTE */
616 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
620 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
624 /* No more subsections, we are done with prototype PTEs */
629 /* Lock the PFN Database while we delete the PTEs */
630 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
633 /* Capture the PDE and make sure it exists */
634 TempPte
= *PointerPte
;
637 MiDecrementPageTableReferences((PVOID
)Va
);
639 /* Check if the PTE is actually mapped in */
640 if (MI_IS_MAPPED_PTE(&TempPte
))
642 /* Are we dealing with section VAD? */
643 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
645 /* We need to skip to the next correct prototype PTE */
646 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
648 /* And we need the subsection to skip to the next last prototype PTE */
649 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
653 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
657 /* No more subsections, we are done with prototype PTEs */
662 /* Check for prototype PTE */
663 if ((TempPte
.u
.Hard
.Valid
== 0) &&
664 (TempPte
.u
.Soft
.Prototype
== 1))
667 MI_ERASE_PTE(PointerPte
);
671 /* Delete the PTE proper */
672 MiDeletePte(PointerPte
,
680 /* The PTE was never mapped, just nuke it here */
681 MI_ERASE_PTE(PointerPte
);
685 /* Update the address and PTE for it */
690 /* Making sure the PDE is still valid */
691 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
693 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
695 /* The PDE should still be valid at this point */
696 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
698 /* Check remaining PTE count (go back 1 page due to above loop) */
699 if (MiQueryPageTableReferences((PVOID
)(Va
- PAGE_SIZE
)) == 0)
701 if (PointerPde
->u
.Long
!= 0)
703 /* Delete the PTE proper */
704 MiDeletePte(PointerPde
,
705 MiPteToAddress(PointerPde
),
711 /* Release the lock and get out if we're done */
712 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
713 if (Va
> EndingAddress
) return;
715 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
716 PointerPde
= MiAddressToPde(Va
);
722 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
723 OUT PBOOLEAN HaveBadAddress
,
724 OUT PULONG_PTR BadAddress
)
726 PEXCEPTION_RECORD ExceptionRecord
;
732 *HaveBadAddress
= FALSE
;
735 // Get the exception record
737 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
740 // Look at the exception code
742 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
743 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
744 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
747 // We can tell the address if we have more than one parameter
749 if (ExceptionRecord
->NumberParameters
> 1)
752 // Return the address
754 *HaveBadAddress
= TRUE
;
755 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
760 // Continue executing the next handler
762 return EXCEPTION_EXECUTE_HANDLER
;
767 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
768 IN PVOID SourceAddress
,
769 IN PEPROCESS TargetProcess
,
770 OUT PVOID TargetAddress
,
771 IN SIZE_T BufferSize
,
772 IN KPROCESSOR_MODE PreviousMode
,
773 OUT PSIZE_T ReturnSize
)
775 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
776 PMDL Mdl
= (PMDL
)MdlBuffer
;
777 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
778 volatile BOOLEAN FailedInProbe
= FALSE
;
779 volatile BOOLEAN PagesLocked
= FALSE
;
780 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
781 volatile PVOID MdlAddress
= NULL
;
783 BOOLEAN HaveBadAddress
;
784 ULONG_PTR BadAddress
;
785 NTSTATUS Status
= STATUS_SUCCESS
;
789 // Calculate the maximum amount of data to move
791 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
792 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
793 CurrentSize
= TotalSize
;
794 RemainingSize
= BufferSize
;
797 // Loop as long as there is still data
799 while (RemainingSize
> 0)
802 // Check if this transfer will finish everything off
804 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
807 // Attach to the source address space
809 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
812 // Check state for this pass
814 ASSERT(MdlAddress
== NULL
);
815 ASSERT(PagesLocked
== FALSE
);
816 ASSERT(FailedInProbe
== FALSE
);
819 // Protect user-mode copy
824 // If this is our first time, probe the buffer
826 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
829 // Catch a failure here
831 FailedInProbe
= TRUE
;
836 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
841 FailedInProbe
= FALSE
;
845 // Initialize and probe and lock the MDL
847 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
848 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
853 Status
= _SEH2_GetExceptionCode();
857 /* Detach from source process */
858 KeUnstackDetachProcess(&ApcState
);
860 if (Status
!= STATUS_SUCCESS
)
868 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
876 Status
= STATUS_INSUFFICIENT_RESOURCES
;
881 // Grab to the target process
883 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
888 // Check if this is our first time through
890 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
893 // Catch a failure here
895 FailedInProbe
= TRUE
;
900 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
905 FailedInProbe
= FALSE
;
909 // Now do the actual move
911 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
913 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
917 *ReturnSize
= BufferSize
- RemainingSize
;
919 // Check if we failed during the probe
926 Status
= _SEH2_GetExceptionCode();
931 // Othewise we failed during the move.
932 // Check if we know exactly where we stopped copying
937 // Return the exact number of bytes copied
939 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
942 // Return partial copy
944 Status
= STATUS_PARTIAL_COPY
;
949 /* Detach from target process */
950 KeUnstackDetachProcess(&ApcState
);
953 // Check for SEH status
955 if (Status
!= STATUS_SUCCESS
)
963 MmUnmapLockedPages(MdlAddress
, Mdl
);
969 // Update location and size
971 RemainingSize
-= CurrentSize
;
972 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
973 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
977 if (MdlAddress
!= NULL
)
978 MmUnmapLockedPages(MdlAddress
, Mdl
);
985 if (Status
== STATUS_SUCCESS
)
986 *ReturnSize
= BufferSize
;
992 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
993 IN PVOID SourceAddress
,
994 IN PEPROCESS TargetProcess
,
995 OUT PVOID TargetAddress
,
996 IN SIZE_T BufferSize
,
997 IN KPROCESSOR_MODE PreviousMode
,
998 OUT PSIZE_T ReturnSize
)
1000 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
1001 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
1002 volatile BOOLEAN FailedInProbe
= FALSE
, HavePoolAddress
= FALSE
;
1003 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
1005 KAPC_STATE ApcState
;
1006 BOOLEAN HaveBadAddress
;
1007 ULONG_PTR BadAddress
;
1008 NTSTATUS Status
= STATUS_SUCCESS
;
1011 DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1012 BufferSize
, SourceProcess
, SourceAddress
, TargetProcess
, TargetAddress
);
1015 // Calculate the maximum amount of data to move
1017 TotalSize
= MI_MAX_TRANSFER_SIZE
;
1018 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
1019 CurrentSize
= TotalSize
;
1020 RemainingSize
= BufferSize
;
1023 // Check if we can use the stack
1025 if (BufferSize
<= MI_POOL_COPY_BYTES
)
1030 PoolAddress
= (PVOID
)StackBuffer
;
1037 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1038 if (!PoolAddress
) ASSERT(FALSE
);
1039 HavePoolAddress
= TRUE
;
1043 // Loop as long as there is still data
1045 while (RemainingSize
> 0)
1048 // Check if this transfer will finish everything off
1050 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1053 // Attach to the source address space
1055 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1057 /* Check that state is sane */
1058 ASSERT(FailedInProbe
== FALSE
);
1059 ASSERT(Status
== STATUS_SUCCESS
);
1062 // Protect user-mode copy
1067 // If this is our first time, probe the buffer
1069 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1072 // Catch a failure here
1074 FailedInProbe
= TRUE
;
1079 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1084 FailedInProbe
= FALSE
;
1090 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1092 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1096 *ReturnSize
= BufferSize
- RemainingSize
;
1099 // Check if we failed during the probe
1106 Status
= _SEH2_GetExceptionCode();
1111 // We failed during the move.
1112 // Check if we know exactly where we stopped copying
1117 // Return the exact number of bytes copied
1119 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1122 // Return partial copy
1124 Status
= STATUS_PARTIAL_COPY
;
1129 /* Let go of the source */
1130 KeUnstackDetachProcess(&ApcState
);
1132 if (Status
!= STATUS_SUCCESS
)
1137 /* Grab the target process */
1138 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1143 // Check if this is our first time through
1145 if ((CurrentTargetAddress
== TargetAddress
) && (PreviousMode
!= KernelMode
))
1148 // Catch a failure here
1150 FailedInProbe
= TRUE
;
1155 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1160 FailedInProbe
= FALSE
;
1164 // Now do the actual move
1166 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1168 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1172 *ReturnSize
= BufferSize
- RemainingSize
;
1174 // Check if we failed during the probe
1181 Status
= _SEH2_GetExceptionCode();
1186 // Otherwise we failed during the move.
1187 // Check if we know exactly where we stopped copying
1192 // Return the exact number of bytes copied
1194 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1197 // Return partial copy
1199 Status
= STATUS_PARTIAL_COPY
;
1205 // Detach from target
1207 KeUnstackDetachProcess(&ApcState
);
1210 // Check for SEH status
1212 if (Status
!= STATUS_SUCCESS
)
1218 // Update location and size
1220 RemainingSize
-= CurrentSize
;
1221 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1222 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1228 // Check if we had allocated pool
1230 if (HavePoolAddress
)
1231 ExFreePoolWithTag(PoolAddress
, 'VmRw');
1236 if (Status
== STATUS_SUCCESS
)
1237 *ReturnSize
= BufferSize
;
1243 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1244 IN PVOID SourceAddress
,
1245 IN PEPROCESS TargetProcess
,
1246 OUT PVOID TargetAddress
,
1247 IN SIZE_T BufferSize
,
1248 IN KPROCESSOR_MODE PreviousMode
,
1249 OUT PSIZE_T ReturnSize
)
1252 PEPROCESS Process
= SourceProcess
;
1255 // Don't accept zero-sized buffers
1257 if (!BufferSize
) return STATUS_SUCCESS
;
1260 // If we are copying from ourselves, lock the target instead
1262 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1265 // Acquire rundown protection
1267 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1272 return STATUS_PROCESS_IS_TERMINATING
;
1276 // See if we should use the pool copy
1278 if (BufferSize
> MI_POOL_COPY_BYTES
)
1283 Status
= MiDoMappedCopy(SourceProcess
,
1296 Status
= MiDoPoolCopy(SourceProcess
,
1308 ExReleaseRundownProtection(&Process
->RundownProtect
);
1314 MmFlushVirtualMemory(IN PEPROCESS Process
,
1315 IN OUT PVOID
*BaseAddress
,
1316 IN OUT PSIZE_T RegionSize
,
1317 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1325 return STATUS_SUCCESS
;
1330 MiGetPageProtection(IN PMMPTE PointerPte
)
1334 PEPROCESS CurrentProcess
;
1335 PETHREAD CurrentThread
;
1336 BOOLEAN WsSafe
, WsShared
;
1341 /* Copy this PTE's contents */
1342 TempPte
= *PointerPte
;
1344 /* Assure it's not totally zero */
1345 ASSERT(TempPte
.u
.Long
);
1347 /* Check for a special prototype format */
1348 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1349 (TempPte
.u
.Soft
.Prototype
== 1))
1351 /* Check if the prototype PTE is not yet pointing to a PTE */
1352 if (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)
1354 /* The prototype PTE contains the protection */
1355 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1358 /* Get a pointer to the underlying shared PTE */
1359 PointerPte
= MiProtoPteToPte(&TempPte
);
1361 /* Since the PTE we want to read can be paged out at any time, we need
1362 to release the working set lock first, so that it can be paged in */
1363 CurrentThread
= PsGetCurrentThread();
1364 CurrentProcess
= PsGetCurrentProcess();
1365 MiUnlockProcessWorkingSetForFault(CurrentProcess
,
1370 /* Now read the PTE value */
1371 TempPte
= *PointerPte
;
1373 /* Check if that one is invalid */
1374 if (!TempPte
.u
.Hard
.Valid
)
1376 /* We get the protection directly from this PTE */
1377 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1381 /* The PTE is valid, so we might need to get the protection from
1382 the PFN. Lock the PFN database */
1383 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1385 /* Check if the PDE is still valid */
1386 if (MiAddressToPte(PointerPte
)->u
.Hard
.Valid
== 0)
1388 /* It's not, make it valid */
1389 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
1392 /* Now it's safe to read the PTE value again */
1393 TempPte
= *PointerPte
;
1394 ASSERT(TempPte
.u
.Long
!= 0);
1396 /* Check again if the PTE is invalid */
1397 if (!TempPte
.u
.Hard
.Valid
)
1399 /* The PTE is not valid, so we can use it's protection field */
1400 Protect
= MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1404 /* The PTE is valid, so we can find the protection in the
1405 OriginalPte field of the PFN */
1406 Pfn
= MI_PFN_ELEMENT(TempPte
.u
.Hard
.PageFrameNumber
);
1407 Protect
= MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1410 /* Release the PFN database */
1411 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1414 /* Lock the working set again */
1415 MiLockProcessWorkingSetForFault(CurrentProcess
,
1423 /* In the easy case of transition or demand zero PTE just return its protection */
1424 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1426 /* If we get here, the PTE is valid, so look up the page in PFN database */
1427 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1428 if (!Pfn
->u3
.e1
.PrototypePte
)
1430 /* Return protection of the original pte */
1431 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1432 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1435 /* This is software PTE */
1436 DPRINT("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1437 DPRINT("VA: %p\n", MiPteToAddress(&TempPte
));
1438 DPRINT("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1439 DPRINT("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1440 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1445 MiQueryAddressState(IN PVOID Va
,
1447 IN PEPROCESS TargetProcess
,
1448 OUT PULONG ReturnedProtect
,
1452 PMMPTE PointerPte
, ProtoPte
;
1454 #if (_MI_PAGING_LEVELS >= 3)
1457 #if (_MI_PAGING_LEVELS >= 4)
1460 MMPTE TempPte
, TempProtoPte
;
1461 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1462 ULONG State
= MEM_RESERVE
, Protect
= 0;
1463 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1464 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1466 /* Only normal VADs supported */
1467 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1469 /* Get the PDE and PTE for the address */
1470 PointerPde
= MiAddressToPde(Va
);
1471 PointerPte
= MiAddressToPte(Va
);
1472 #if (_MI_PAGING_LEVELS >= 3)
1473 PointerPpe
= MiAddressToPpe(Va
);
1475 #if (_MI_PAGING_LEVELS >= 4)
1476 PointerPxe
= MiAddressToPxe(Va
);
1479 /* Return the next range */
1480 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1484 #if (_MI_PAGING_LEVELS >= 4)
1485 /* Does the PXE exist? */
1486 if (PointerPxe
->u
.Long
== 0)
1488 /* It does not, next range starts at the next PXE */
1489 *NextVa
= MiPxeToAddress(PointerPxe
+ 1);
1493 /* Is the PXE valid? */
1494 if (PointerPxe
->u
.Hard
.Valid
== 0)
1496 /* Is isn't, fault it in (make the PPE accessible) */
1497 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1500 #if (_MI_PAGING_LEVELS >= 3)
1501 /* Does the PPE exist? */
1502 if (PointerPpe
->u
.Long
== 0)
1504 /* It does not, next range starts at the next PPE */
1505 *NextVa
= MiPpeToAddress(PointerPpe
+ 1);
1509 /* Is the PPE valid? */
1510 if (PointerPpe
->u
.Hard
.Valid
== 0)
1512 /* Is isn't, fault it in (make the PDE accessible) */
1513 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1517 /* Does the PDE exist? */
1518 if (PointerPde
->u
.Long
== 0)
1520 /* It does not, next range starts at the next PDE */
1521 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1525 /* Is the PDE valid? */
1526 if (PointerPde
->u
.Hard
.Valid
== 0)
1528 /* Is isn't, fault it in (make the PTE accessible) */
1529 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1532 /* We have a PTE that we can access now! */
1537 /* Is it safe to try reading the PTE? */
1540 /* FIXME: watch out for large pages */
1541 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1543 /* Capture the PTE */
1544 TempPte
= *PointerPte
;
1545 if (TempPte
.u
.Long
!= 0)
1547 /* The PTE is valid, so it's not zeroed out */
1548 DemandZeroPte
= FALSE
;
1550 /* Is it a decommited, invalid, or faulted PTE? */
1551 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1552 (TempPte
.u
.Hard
.Valid
== 0) &&
1553 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1554 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1556 /* Otherwise our defaults should hold */
1557 ASSERT(Protect
== 0);
1558 ASSERT(State
== MEM_RESERVE
);
1562 /* This means it's committed */
1565 /* We don't support these */
1566 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1567 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1568 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1570 /* Get protection state of this page */
1571 Protect
= MiGetPageProtection(PointerPte
);
1573 /* Check if this is an image-backed VAD */
1574 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1575 (TempPte
.u
.Soft
.Prototype
== 1) &&
1576 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1579 DPRINT1("Not supported\n");
1586 /* Check if this was a demand-zero PTE, since we need to find the state */
1589 /* Not yet handled */
1590 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1591 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1593 /* Check if this is private commited memory, or an section-backed VAD */
1594 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1596 /* Tell caller about the next range */
1597 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1599 /* Get the prototype PTE for this VAD */
1600 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1601 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1604 /* We should unlock the working set, but it's not being held! */
1606 /* Is the prototype PTE actually valid (committed)? */
1607 TempProtoPte
= *ProtoPte
;
1608 if (TempProtoPte
.u
.Long
)
1610 /* Unless this is a memory-mapped file, handle it like private VAD */
1612 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1613 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1616 /* We should re-lock the working set */
1619 else if (Vad
->u
.VadFlags
.MemCommit
)
1621 /* This is committed memory */
1624 /* Convert the protection */
1625 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1629 /* Return the protection code */
1630 *ReturnedProtect
= Protect
;
1636 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1637 IN PVOID BaseAddress
,
1638 OUT PVOID MemoryInformation
,
1639 IN SIZE_T MemoryInformationLength
,
1640 OUT PSIZE_T ReturnLength
)
1642 PEPROCESS TargetProcess
;
1643 NTSTATUS Status
= STATUS_SUCCESS
;
1645 PVOID Address
, NextAddress
;
1646 BOOLEAN Found
= FALSE
;
1647 ULONG NewProtect
, NewState
;
1649 MEMORY_BASIC_INFORMATION MemoryInfo
;
1650 KAPC_STATE ApcState
;
1651 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1652 PMEMORY_AREA MemoryArea
;
1653 SIZE_T ResultLength
;
1655 /* Check for illegal addresses in user-space, or the shared memory area */
1656 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1657 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1659 Address
= PAGE_ALIGN(BaseAddress
);
1661 /* Make up an info structure describing this range */
1662 MemoryInfo
.BaseAddress
= Address
;
1663 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1664 MemoryInfo
.Type
= MEM_PRIVATE
;
1666 /* Special case for shared data */
1667 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1669 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1670 MemoryInfo
.State
= MEM_COMMIT
;
1671 MemoryInfo
.Protect
= PAGE_READONLY
;
1672 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1676 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1677 MemoryInfo
.State
= MEM_RESERVE
;
1678 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1679 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1682 /* Return the data, NtQueryInformation already probed it*/
1683 if (PreviousMode
!= KernelMode
)
1687 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1688 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1690 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1692 Status
= _SEH2_GetExceptionCode();
1698 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1699 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1705 /* Check if this is for a local or remote process */
1706 if (ProcessHandle
== NtCurrentProcess())
1708 TargetProcess
= PsGetCurrentProcess();
1712 /* Reference the target process */
1713 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1714 PROCESS_QUERY_INFORMATION
,
1716 ExGetPreviousMode(),
1717 (PVOID
*)&TargetProcess
,
1719 if (!NT_SUCCESS(Status
)) return Status
;
1721 /* Attach to it now */
1722 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1725 /* Lock the address space and make sure the process isn't already dead */
1726 MmLockAddressSpace(&TargetProcess
->Vm
);
1727 if (TargetProcess
->VmDeleted
)
1729 /* Unlock the address space of the process */
1730 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1732 /* Check if we were attached */
1733 if (ProcessHandle
!= NtCurrentProcess())
1735 /* Detach and dereference the process */
1736 KeUnstackDetachProcess(&ApcState
);
1737 ObDereferenceObject(TargetProcess
);
1741 DPRINT1("Process is dying\n");
1742 return STATUS_PROCESS_IS_TERMINATING
;
1746 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1747 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1749 /* Scan on the right */
1750 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1751 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1754 /* Check if this VAD covers the allocation range */
1755 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1756 (BaseVpn
<= Vad
->EndingVpn
))
1763 /* Check if this VAD is too high */
1764 if (BaseVpn
< Vad
->StartingVpn
)
1766 /* Stop if there is no left child */
1767 if (!Vad
->LeftChild
) break;
1769 /* Search on the left next */
1770 Vad
= Vad
->LeftChild
;
1774 /* Then this VAD is too low, keep searching on the right */
1775 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1777 /* Stop if there is no right child */
1778 if (!Vad
->RightChild
) break;
1780 /* Search on the right next */
1781 Vad
= Vad
->RightChild
;
1786 /* Was a VAD found? */
1789 Address
= PAGE_ALIGN(BaseAddress
);
1791 /* Calculate region size */
1794 if (Vad
->StartingVpn
>= BaseVpn
)
1796 /* Region size is the free space till the start of that VAD */
1797 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1801 /* Get the next VAD */
1802 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1805 /* Region size is the free space till the start of that VAD */
1806 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1810 /* Maximum possible region size with that base address */
1811 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1817 /* Maximum possible region size with that base address */
1818 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1821 /* Unlock the address space of the process */
1822 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1824 /* Check if we were attached */
1825 if (ProcessHandle
!= NtCurrentProcess())
1827 /* Detach and derefernece the process */
1828 KeUnstackDetachProcess(&ApcState
);
1829 ObDereferenceObject(TargetProcess
);
1832 /* Build the rest of the initial information block */
1833 MemoryInfo
.BaseAddress
= Address
;
1834 MemoryInfo
.AllocationBase
= NULL
;
1835 MemoryInfo
.AllocationProtect
= 0;
1836 MemoryInfo
.State
= MEM_FREE
;
1837 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1838 MemoryInfo
.Type
= 0;
1840 /* Return the data, NtQueryInformation already probed it*/
1841 if (PreviousMode
!= KernelMode
)
1845 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1846 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1848 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1850 Status
= _SEH2_GetExceptionCode();
1856 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1857 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1863 /* Set the correct memory type based on what kind of VAD this is */
1864 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1865 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1867 MemoryInfo
.Type
= MEM_PRIVATE
;
1869 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1871 MemoryInfo
.Type
= MEM_IMAGE
;
1875 MemoryInfo
.Type
= MEM_MAPPED
;
1878 /* Find the memory area the specified address belongs to */
1879 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1880 ASSERT(MemoryArea
!= NULL
);
1882 /* Determine information dependent on the memory area type */
1883 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1885 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1886 if (!NT_SUCCESS(Status
))
1888 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1889 MemoryArea
, MA_GetStartingAddress(MemoryArea
), MA_GetEndingAddress(MemoryArea
), BaseAddress
);
1890 NT_ASSERT(NT_SUCCESS(Status
));
1895 /* Build the initial information block */
1896 Address
= PAGE_ALIGN(BaseAddress
);
1897 MemoryInfo
.BaseAddress
= Address
;
1898 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1899 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1900 MemoryInfo
.Type
= MEM_PRIVATE
;
1902 /* Acquire the working set lock (shared is enough) */
1903 MiLockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1905 /* Find the largest chunk of memory which has the same state and protection mask */
1906 MemoryInfo
.State
= MiQueryAddressState(Address
,
1909 &MemoryInfo
.Protect
,
1911 Address
= NextAddress
;
1912 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1914 /* Keep going unless the state or protection mask changed */
1915 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1916 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1917 Address
= NextAddress
;
1920 /* Release the working set lock */
1921 MiUnlockProcessWorkingSetShared(TargetProcess
, PsGetCurrentThread());
1923 /* Check if we went outside of the VAD */
1924 if (((ULONG_PTR
)Address
>> PAGE_SHIFT
) > Vad
->EndingVpn
)
1926 /* Set the end of the VAD as the end address */
1927 Address
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
1930 /* Now that we know the last VA address, calculate the region size */
1931 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1934 /* Unlock the address space of the process */
1935 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1937 /* Check if we were attached */
1938 if (ProcessHandle
!= NtCurrentProcess())
1940 /* Detach and derefernece the process */
1941 KeUnstackDetachProcess(&ApcState
);
1942 ObDereferenceObject(TargetProcess
);
1945 /* Return the data, NtQueryInformation already probed it */
1946 if (PreviousMode
!= KernelMode
)
1950 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1951 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1953 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1955 Status
= _SEH2_GetExceptionCode();
1961 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1962 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1966 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1967 "State: %lx Type: %lx Size: %lx\n",
1968 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1969 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1970 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1977 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1978 IN ULONG_PTR EndingAddress
,
1980 IN PEPROCESS Process
)
1982 PMMPTE PointerPte
, LastPte
;
1984 BOOLEAN OnBoundary
= TRUE
;
1987 /* Get the PDE and PTE addresses */
1988 PointerPde
= MiAddressToPde(StartingAddress
);
1989 PointerPte
= MiAddressToPte(StartingAddress
);
1990 LastPte
= MiAddressToPte(EndingAddress
);
1992 /* Loop all the PTEs */
1993 while (PointerPte
<= LastPte
)
1995 /* Check if we've hit an new PDE boundary */
1998 /* Is this PDE demand zero? */
1999 PointerPde
= MiPteToPde(PointerPte
);
2000 if (PointerPde
->u
.Long
!= 0)
2002 /* It isn't -- is it valid? */
2003 if (PointerPde
->u
.Hard
.Valid
== 0)
2005 /* Nope, fault it in */
2006 MiMakeSystemAddressValid(PointerPte
, Process
);
2011 /* The PTE was already valid, so move to the next one */
2013 PointerPte
= MiPdeToPte(PointerPde
);
2015 /* Is the entire VAD committed? If not, fail */
2016 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2018 /* New loop iteration with our new, on-boundary PTE. */
2023 /* Is the PTE demand zero? */
2024 if (PointerPte
->u
.Long
== 0)
2026 /* Is the entire VAD committed? If not, fail */
2027 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
2031 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2032 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
2033 (PointerPte
->u
.Hard
.Valid
== 0) &&
2034 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
2035 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
2037 /* Then part of the range is decommitted, so fail */
2042 /* Move to the next PTE */
2044 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
2047 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2053 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
2054 IN OUT PVOID
*BaseAddress
,
2055 IN OUT PSIZE_T NumberOfBytesToProtect
,
2056 IN ULONG NewAccessProtection
,
2057 OUT PULONG OldAccessProtection OPTIONAL
)
2059 PMEMORY_AREA MemoryArea
;
2060 PMMSUPPORT AddressSpace
;
2061 ULONG OldAccessProtection_
;
2064 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
2065 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
2067 AddressSpace
= &Process
->Vm
;
2068 MmLockAddressSpace(AddressSpace
);
2069 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
2070 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
2072 MmUnlockAddressSpace(AddressSpace
);
2073 return STATUS_UNSUCCESSFUL
;
2076 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
2078 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
2079 Status
= MmProtectSectionView(AddressSpace
,
2082 *NumberOfBytesToProtect
,
2083 NewAccessProtection
,
2084 OldAccessProtection
);
2086 MmUnlockAddressSpace(AddressSpace
);
2093 MiProtectVirtualMemory(IN PEPROCESS Process
,
2094 IN OUT PVOID
*BaseAddress
,
2095 IN OUT PSIZE_T NumberOfBytesToProtect
,
2096 IN ULONG NewAccessProtection
,
2097 OUT PULONG OldAccessProtection OPTIONAL
)
2099 PMEMORY_AREA MemoryArea
;
2101 PMMSUPPORT AddressSpace
;
2102 ULONG_PTR StartingAddress
, EndingAddress
;
2103 PMMPTE PointerPte
, LastPte
;
2107 ULONG ProtectionMask
, OldProtect
;
2109 NTSTATUS Status
= STATUS_SUCCESS
;
2110 PETHREAD Thread
= PsGetCurrentThread();
2111 TABLE_SEARCH_RESULT Result
;
2113 /* Calculate base address for the VAD */
2114 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
2115 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
2117 /* Calculate the protection mask and make sure it's valid */
2118 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
2119 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2121 DPRINT1("Invalid protection mask\n");
2122 return STATUS_INVALID_PAGE_PROTECTION
;
2125 /* Check for ROS specific memory area */
2126 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
2127 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2130 return MiRosProtectVirtualMemory(Process
,
2132 NumberOfBytesToProtect
,
2133 NewAccessProtection
,
2134 OldAccessProtection
);
2137 /* Lock the address space and make sure the process isn't already dead */
2138 AddressSpace
= MmGetCurrentAddressSpace();
2139 MmLockAddressSpace(AddressSpace
);
2140 if (Process
->VmDeleted
)
2142 DPRINT1("Process is dying\n");
2143 Status
= STATUS_PROCESS_IS_TERMINATING
;
2147 /* Get the VAD for this address range, and make sure it exists */
2148 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
2149 EndingAddress
>> PAGE_SHIFT
,
2151 (PMMADDRESS_NODE
*)&Vad
);
2152 if (Result
!= TableFoundNode
)
2154 DPRINT("Could not find a VAD for this allocation\n");
2155 Status
= STATUS_CONFLICTING_ADDRESSES
;
2159 /* Make sure the address is within this VAD's boundaries */
2160 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
2161 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
2163 Status
= STATUS_CONFLICTING_ADDRESSES
;
2167 /* These kinds of VADs are not supported atm */
2168 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
2169 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
2170 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
2172 DPRINT1("Illegal VAD for attempting to set protection\n");
2173 Status
= STATUS_CONFLICTING_ADDRESSES
;
2177 /* Check for a VAD whose protection can't be changed */
2178 if (Vad
->u
.VadFlags
.NoChange
== 1)
2180 DPRINT1("Trying to change protection of a NoChange VAD\n");
2181 Status
= STATUS_INVALID_PAGE_PROTECTION
;
2185 /* Is this section, or private memory? */
2186 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
2188 /* Not yet supported */
2189 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
2191 DPRINT1("Illegal VAD for attempting to set protection\n");
2192 Status
= STATUS_CONFLICTING_ADDRESSES
;
2196 /* Rotate VADs are not yet supported */
2197 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
2199 DPRINT1("Illegal VAD for attempting to set protection\n");
2200 Status
= STATUS_CONFLICTING_ADDRESSES
;
2204 /* Not valid on section files */
2205 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
2208 DPRINT1("Invalid protection flags for section\n");
2209 Status
= STATUS_INVALID_PARAMETER_4
;
2213 /* Check if data or page file mapping protection PTE is compatible */
2214 if (!Vad
->ControlArea
->u
.Flags
.Image
)
2217 DPRINT1("Fixme: Not checking for valid protection\n");
2220 /* This is a section, and this is not yet supported */
2221 DPRINT1("Section protection not yet supported\n");
2226 /* Private memory, check protection flags */
2227 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2228 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2230 DPRINT1("Invalid protection flags for private memory\n");
2231 Status
= STATUS_INVALID_PARAMETER_4
;
2235 /* Lock the working set */
2236 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2238 /* Check if all pages in this range are committed */
2239 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2246 DPRINT1("The entire range is not committed\n");
2247 Status
= STATUS_NOT_COMMITTED
;
2248 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2252 /* Compute starting and ending PTE and PDE addresses */
2253 PointerPde
= MiAddressToPde(StartingAddress
);
2254 PointerPte
= MiAddressToPte(StartingAddress
);
2255 LastPte
= MiAddressToPte(EndingAddress
);
2257 /* Make this PDE valid */
2258 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2260 /* Save protection of the first page */
2261 if (PointerPte
->u
.Long
!= 0)
2263 /* Capture the page protection and make the PDE valid */
2264 OldProtect
= MiGetPageProtection(PointerPte
);
2265 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2269 /* Grab the old protection from the VAD itself */
2270 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2273 /* Loop all the PTEs now */
2274 while (PointerPte
<= LastPte
)
2276 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2277 if (MiIsPteOnPdeBoundary(PointerPte
))
2279 PointerPde
= MiPteToPde(PointerPte
);
2280 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2283 /* Capture the PTE and check if it was empty */
2284 PteContents
= *PointerPte
;
2285 if (PteContents
.u
.Long
== 0)
2287 /* This used to be a zero PTE and it no longer is, so we must add a
2288 reference to the pagetable. */
2289 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2292 /* Check what kind of PTE we are dealing with */
2293 if (PteContents
.u
.Hard
.Valid
== 1)
2295 /* Get the PFN entry */
2296 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2298 /* We don't support this yet */
2299 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2301 /* Check if the page should not be accessible at all */
2302 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2303 (NewAccessProtection
& PAGE_GUARD
))
2305 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2307 /* Mark the PTE as transition and change its protection */
2308 PteContents
.u
.Hard
.Valid
= 0;
2309 PteContents
.u
.Soft
.Transition
= 1;
2310 PteContents
.u
.Trans
.Protection
= ProtectionMask
;
2311 /* Decrease PFN share count and write the PTE */
2312 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2313 // FIXME: remove the page from the WS
2314 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2316 // FIXME: Should invalidate entry in every CPU TLB
2319 KeInvalidateTlbEntry(MiPteToAddress(PointerPte
));
2321 /* We are done for this PTE */
2322 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2326 /* Write the protection mask and write it with a TLB flush */
2327 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2328 MiFlushTbAndCapture(Vad
,
2337 /* We don't support these cases yet */
2338 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2339 //ASSERT(PteContents.u.Soft.Transition == 0);
2341 /* The PTE is already demand-zero, just update the protection mask */
2342 PteContents
.u
.Soft
.Protection
= ProtectionMask
;
2343 MI_WRITE_INVALID_PTE(PointerPte
, PteContents
);
2344 ASSERT(PointerPte
->u
.Long
!= 0);
2347 /* Move to the next PTE */
2351 /* Unlock the working set */
2352 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2355 /* Unlock the address space */
2356 MmUnlockAddressSpace(AddressSpace
);
2358 /* Return parameters and success */
2359 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2360 *BaseAddress
= (PVOID
)StartingAddress
;
2361 *OldAccessProtection
= OldProtect
;
2362 return STATUS_SUCCESS
;
2365 /* Unlock the address space and return the failure code */
2366 MmUnlockAddressSpace(AddressSpace
);
2372 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde
,
2373 IN PEPROCESS TargetProcess
,
2376 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2379 // Sanity checks. The latter is because we only use this function with the
2380 // PFN lock not held, so it may go away in the future.
2382 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2383 ASSERT(OldIrql
== MM_NOIRQL
);
2386 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2387 // return the same address as the PDE on 2-level page table systems.
2389 // If everything is already valid, there is nothing to do.
2391 PointerPpe
= MiAddressToPte(PointerPde
);
2392 PointerPxe
= MiAddressToPde(PointerPde
);
2393 if ((PointerPxe
->u
.Hard
.Valid
) &&
2394 (PointerPpe
->u
.Hard
.Valid
) &&
2395 (PointerPde
->u
.Hard
.Valid
))
2401 // At least something is invalid, so begin by getting the PTE for the PDE itself
2402 // and then lookup each additional level. We must do it in this precise order
2403 // because the pagfault.c code (as well as in Windows) depends that the next
2404 // level up (higher) must be valid when faulting a lower level
2406 PointerPte
= MiPteToAddress(PointerPde
);
2410 // Make sure APCs continued to be disabled
2412 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2415 // First, make the PXE valid if needed
2417 if (!PointerPxe
->u
.Hard
.Valid
)
2419 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2420 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2426 if (!PointerPpe
->u
.Hard
.Valid
)
2428 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2429 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2433 // And finally, make the PDE itself valid.
2435 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2438 // This should've worked the first time so the loop is really just for
2439 // show -- ASSERT that we're actually NOT going to be looping.
2441 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2442 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2443 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2444 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2445 !(PointerPpe
->u
.Hard
.Valid
) ||
2446 !(PointerPde
->u
.Hard
.Valid
));
2451 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2457 PFN_NUMBER PageFrameIndex
;
2461 // Acquire the PFN lock and loop all the PTEs in the list
2463 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2464 for (i
= 0; i
!= Count
; i
++)
2467 // The PTE must currently be valid
2469 TempPte
= *ValidPteList
[i
];
2470 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2473 // Get the PFN entry for the page itself, and then for its page table
2475 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2476 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2477 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2480 // Decrement the share count on the page table, and then on the page
2483 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2484 MI_SET_PFN_DELETED(Pfn1
);
2485 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2488 // Make the page decommitted
2490 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2494 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2495 // and then release the PFN lock
2498 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2503 MiDecommitPages(IN PVOID StartingAddress
,
2504 IN PMMPTE EndingPte
,
2505 IN PEPROCESS Process
,
2508 PMMPTE PointerPte
, CommitPte
= NULL
;
2510 ULONG CommitReduction
= 0;
2511 PMMPTE ValidPteList
[256];
2515 PETHREAD CurrentThread
= PsGetCurrentThread();
2518 // Get the PTE and PTE for the address, and lock the working set
2519 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2520 // commited range ends so that we can do the right accounting.
2522 PointerPde
= MiAddressToPde(StartingAddress
);
2523 PointerPte
= MiAddressToPte(StartingAddress
);
2524 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2525 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2528 // Make the PDE valid, and now loop through each page's worth of data
2530 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2531 while (PointerPte
<= EndingPte
)
2534 // Check if we've crossed a PDE boundary
2536 if (MiIsPteOnPdeBoundary(PointerPte
))
2539 // Get the new PDE and flush the valid PTEs we had built up until
2540 // now. This helps reduce the amount of TLB flushing we have to do.
2541 // Note that Windows does a much better job using timestamps and
2542 // such, and does not flush the entire TLB all the time, but right
2543 // now we have bigger problems to worry about than TLB flushing.
2545 PointerPde
= MiAddressToPde(StartingAddress
);
2548 MiProcessValidPteList(ValidPteList
, PteCount
);
2553 // Make this PDE valid
2555 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2559 // Read this PTE. It might be active or still demand-zero.
2561 PteContents
= *PointerPte
;
2562 if (PteContents
.u
.Long
)
2565 // The PTE is active. It might be valid and in a working set, or
2566 // it might be a prototype PTE or paged out or even in transition.
2568 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2571 // It's already decommited, so there's nothing for us to do here
2578 // Remove it from the counters, and check if it was valid or not
2580 //Process->NumberOfPrivatePages--;
2581 if (PteContents
.u
.Hard
.Valid
)
2584 // It's valid. At this point make sure that it is not a ROS
2585 // PFN. Also, we don't support ProtoPTEs in this code path.
2587 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2588 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2589 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2592 // Flush any pending PTEs that we had not yet flushed, if our
2593 // list has gotten too big, then add this PTE to the flush list.
2595 if (PteCount
== 256)
2597 MiProcessValidPteList(ValidPteList
, PteCount
);
2600 ValidPteList
[PteCount
++] = PointerPte
;
2605 // We do not support any of these other scenarios at the moment
2607 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2608 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2609 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2612 // So the only other possibility is that it is still a demand
2613 // zero PTE, in which case we undo the accounting we did
2614 // earlier and simply make the page decommitted.
2616 //Process->NumberOfPrivatePages++;
2617 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2624 // This used to be a zero PTE and it no longer is, so we must add a
2625 // reference to the pagetable.
2627 MiIncrementPageTableReferences(StartingAddress
);
2630 // Next, we account for decommitted PTEs and make the PTE as such
2632 if (PointerPte
> CommitPte
) CommitReduction
++;
2633 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2637 // Move to the next PTE and the next address
2640 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2644 // Flush any dangling PTEs from the loop in the last page table, and then
2645 // release the working set and return the commit reduction accounting.
2647 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2648 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
2649 return CommitReduction
;
2652 /* PUBLIC FUNCTIONS ***********************************************************/
2659 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2670 MmSecureVirtualMemory(IN PVOID Address
,
2674 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2683 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2685 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2688 /* SYSTEM CALLS ***************************************************************/
2692 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2693 IN PVOID BaseAddress
,
2695 IN SIZE_T NumberOfBytesToRead
,
2696 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2698 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2700 NTSTATUS Status
= STATUS_SUCCESS
;
2701 SIZE_T BytesRead
= 0;
2705 // Check if we came from user mode
2707 if (PreviousMode
!= KernelMode
)
2710 // Validate the read addresses
2712 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2713 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2714 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2715 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2718 // Don't allow to write into kernel space
2720 return STATUS_ACCESS_VIOLATION
;
2724 // Enter SEH for probe
2729 // Probe the output value
2731 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2733 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2736 // Get exception code
2738 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2744 // Don't do zero-byte transfers
2746 if (NumberOfBytesToRead
)
2749 // Reference the process
2751 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2757 if (NT_SUCCESS(Status
))
2762 Status
= MmCopyVirtualMemory(Process
,
2764 PsGetCurrentProcess(),
2766 NumberOfBytesToRead
,
2771 // Dereference the process
2773 ObDereferenceObject(Process
);
2778 // Check if the caller sent this parameter
2780 if (NumberOfBytesRead
)
2783 // Enter SEH to guard write
2788 // Return the number of bytes read
2790 *NumberOfBytesRead
= BytesRead
;
2792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2806 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2807 IN PVOID BaseAddress
,
2809 IN SIZE_T NumberOfBytesToWrite
,
2810 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2812 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2814 NTSTATUS Status
= STATUS_SUCCESS
;
2815 SIZE_T BytesWritten
= 0;
2819 // Check if we came from user mode
2821 if (PreviousMode
!= KernelMode
)
2824 // Validate the read addresses
2826 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2827 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2828 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2829 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2832 // Don't allow to write into kernel space
2834 return STATUS_ACCESS_VIOLATION
;
2838 // Enter SEH for probe
2843 // Probe the output value
2845 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2850 // Get exception code
2852 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2858 // Don't do zero-byte transfers
2860 if (NumberOfBytesToWrite
)
2863 // Reference the process
2865 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2871 if (NT_SUCCESS(Status
))
2876 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2880 NumberOfBytesToWrite
,
2885 // Dereference the process
2887 ObDereferenceObject(Process
);
2892 // Check if the caller sent this parameter
2894 if (NumberOfBytesWritten
)
2897 // Enter SEH to guard write
2902 // Return the number of bytes written
2904 *NumberOfBytesWritten
= BytesWritten
;
2906 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2920 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2921 IN OUT PVOID
*UnsafeBaseAddress
,
2922 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2923 IN ULONG NewAccessProtection
,
2924 OUT PULONG UnsafeOldAccessProtection
)
2927 ULONG OldAccessProtection
;
2929 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2930 PVOID BaseAddress
= NULL
;
2931 SIZE_T NumberOfBytesToProtect
= 0;
2932 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2934 BOOLEAN Attached
= FALSE
;
2935 KAPC_STATE ApcState
;
2939 // Check for valid protection flags
2941 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2942 if (Protection
!= PAGE_NOACCESS
&&
2943 Protection
!= PAGE_READONLY
&&
2944 Protection
!= PAGE_READWRITE
&&
2945 Protection
!= PAGE_WRITECOPY
&&
2946 Protection
!= PAGE_EXECUTE
&&
2947 Protection
!= PAGE_EXECUTE_READ
&&
2948 Protection
!= PAGE_EXECUTE_READWRITE
&&
2949 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2954 return STATUS_INVALID_PAGE_PROTECTION
;
2958 // Check if we came from user mode
2960 if (PreviousMode
!= KernelMode
)
2963 // Enter SEH for probing
2968 // Validate all outputs
2970 ProbeForWritePointer(UnsafeBaseAddress
);
2971 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2972 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2977 BaseAddress
= *UnsafeBaseAddress
;
2978 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2980 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2983 // Get exception code
2985 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2994 BaseAddress
= *UnsafeBaseAddress
;
2995 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2999 // Catch illegal base address
3001 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3004 // Catch illegal region size
3006 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
3011 return STATUS_INVALID_PARAMETER_3
;
3015 // 0 is also illegal
3017 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
3020 // Get a reference to the process
3022 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3023 PROCESS_VM_OPERATION
,
3028 if (!NT_SUCCESS(Status
)) return Status
;
3031 // Check if we should attach
3033 if (CurrentProcess
!= Process
)
3038 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3043 // Do the actual work
3045 Status
= MiProtectVirtualMemory(Process
,
3047 &NumberOfBytesToProtect
,
3048 NewAccessProtection
,
3049 &OldAccessProtection
);
3054 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3057 // Release reference
3059 ObDereferenceObject(Process
);
3062 // Enter SEH to return data
3067 // Return data to user
3069 *UnsafeOldAccessProtection
= OldAccessProtection
;
3070 *UnsafeBaseAddress
= BaseAddress
;
3071 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
3073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3090 // HACK until we have proper WSLIST support
3091 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3093 if ((LockType
& MAP_PROCESS
) && (Wsle
->u1
.e1
.LockedInWs
))
3095 if ((LockType
& MAP_SYSTEM
) && (Wsle
->u1
.e1
.LockedInMemory
))
3107 // HACK until we have proper WSLIST support
3108 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3110 if (!Wsle
->u1
.e1
.LockedInWs
&&
3111 !Wsle
->u1
.e1
.LockedInMemory
)
3113 MiReferenceProbedPageAndBumpLockCount(Pfn1
);
3116 if (LockType
& MAP_PROCESS
)
3117 Wsle
->u1
.e1
.LockedInWs
= 1;
3118 if (LockType
& MAP_SYSTEM
)
3119 Wsle
->u1
.e1
.LockedInMemory
= 1;
3128 // HACK until we have proper WSLIST support
3129 PMMWSLE Wsle
= &Pfn1
->Wsle
;
3131 if (LockType
& MAP_PROCESS
)
3132 Wsle
->u1
.e1
.LockedInWs
= 0;
3133 if (LockType
& MAP_SYSTEM
)
3134 Wsle
->u1
.e1
.LockedInMemory
= 0;
3136 if (!Wsle
->u1
.e1
.LockedInWs
&&
3137 !Wsle
->u1
.e1
.LockedInMemory
)
3139 MiDereferencePfnAndDropLockCount(Pfn1
);
3145 MiCheckVadsForLockOperation(
3146 _Inout_ PVOID
*BaseAddress
,
3147 _Inout_ PSIZE_T RegionSize
,
3148 _Inout_ PVOID
*EndAddress
)
3154 /* Get the base address and align the start address */
3155 *EndAddress
= (PUCHAR
)*BaseAddress
+ *RegionSize
;
3156 *EndAddress
= ALIGN_UP_POINTER_BY(*EndAddress
, PAGE_SIZE
);
3157 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, PAGE_SIZE
);
3159 /* First loop and check all VADs */
3160 CurrentVa
= *BaseAddress
;
3161 while (CurrentVa
< *EndAddress
)
3164 Vad
= MiLocateAddress(CurrentVa
);
3167 /// FIXME: this might be a memory area for a section view...
3168 return STATUS_ACCESS_VIOLATION
;
3171 /* Check VAD type */
3172 if ((Vad
->u
.VadFlags
.VadType
!= VadNone
) &&
3173 (Vad
->u
.VadFlags
.VadType
!= VadImageMap
) &&
3174 (Vad
->u
.VadFlags
.VadType
!= VadWriteWatch
))
3176 *EndAddress
= CurrentVa
;
3177 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3178 return STATUS_INCOMPATIBLE_FILE_MAP
;
3181 CurrentVa
= (PVOID
)((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
);
3184 *RegionSize
= (PUCHAR
)*EndAddress
- (PUCHAR
)*BaseAddress
;
3185 return STATUS_SUCCESS
;
3190 MiLockVirtualMemory(
3191 IN OUT PVOID
*BaseAddress
,
3192 IN OUT PSIZE_T RegionSize
,
3195 PEPROCESS CurrentProcess
;
3196 PMMSUPPORT AddressSpace
;
3197 PVOID CurrentVa
, EndAddress
;
3198 PMMPTE PointerPte
, LastPte
;
3200 #if (_MI_PAGING_LEVELS >= 3)
3203 #if (_MI_PAGING_LEVELS == 4)
3207 NTSTATUS Status
, TempStatus
;
3209 /* Lock the address space */
3210 AddressSpace
= MmGetCurrentAddressSpace();
3211 MmLockAddressSpace(AddressSpace
);
3213 /* Make sure we still have an address space */
3214 CurrentProcess
= PsGetCurrentProcess();
3215 if (CurrentProcess
->VmDeleted
)
3217 Status
= STATUS_PROCESS_IS_TERMINATING
;
3221 /* Check the VADs in the requested range */
3222 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3223 if (!NT_SUCCESS(Status
))
3228 /* Enter SEH for probing */
3231 /* Loop all pages and probe them */
3232 CurrentVa
= *BaseAddress
;
3233 while (CurrentVa
< EndAddress
)
3235 (void)(*(volatile CHAR
*)CurrentVa
);
3236 CurrentVa
= (PUCHAR
)CurrentVa
+ PAGE_SIZE
;
3239 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3241 Status
= _SEH2_GetExceptionCode();
3246 /* All pages were accessible, since we hold the address space lock, nothing
3247 can be de-committed. Assume success for now. */
3248 Status
= STATUS_SUCCESS
;
3250 /* Get the PTE and PDE */
3251 PointerPte
= MiAddressToPte(*BaseAddress
);
3252 PointerPde
= MiAddressToPde(*BaseAddress
);
3253 #if (_MI_PAGING_LEVELS >= 3)
3254 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3256 #if (_MI_PAGING_LEVELS == 4)
3257 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3260 /* Get the last PTE */
3261 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3263 /* Lock the process working set */
3264 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3266 /* Loop the pages */
3269 /* Check for a page that is not accessible */
3271 #if (_MI_PAGING_LEVELS == 4)
3272 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3274 #if (_MI_PAGING_LEVELS >= 3)
3275 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3277 (PointerPde
->u
.Hard
.Valid
== 0) ||
3278 (PointerPte
->u
.Hard
.Valid
== 0))
3280 /* Release process working set */
3281 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3283 /* Access the page */
3284 CurrentVa
= MiPteToAddress(PointerPte
);
3286 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3287 TempStatus
= MmAccessFault(TRUE
, CurrentVa
, KernelMode
, (PVOID
)0xBADBADA3);
3288 if (!NT_SUCCESS(TempStatus
))
3290 // This should only happen, when remote backing storage is not accessible
3292 Status
= TempStatus
;
3296 /* Lock the process working set */
3297 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3301 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3302 ASSERT(Pfn1
!= NULL
);
3304 /* Check the previous lock status */
3305 if (MI_IS_LOCKED_VA(Pfn1
, MapType
))
3307 Status
= STATUS_WAS_LOCKED
;
3311 MI_LOCK_VA(Pfn1
, MapType
);
3313 /* Go to the next PTE */
3316 /* Check if we're on a PDE boundary */
3317 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3318 #if (_MI_PAGING_LEVELS >= 3)
3319 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3321 #if (_MI_PAGING_LEVELS == 4)
3322 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3324 } while (PointerPte
<= LastPte
);
3326 /* Release process working set */
3327 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3330 /* Unlock address space */
3331 MmUnlockAddressSpace(AddressSpace
);
3338 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
3339 IN OUT PVOID
*BaseAddress
,
3340 IN OUT PSIZE_T NumberOfBytesToLock
,
3344 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3346 BOOLEAN Attached
= FALSE
;
3347 KAPC_STATE ApcState
;
3348 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3349 PVOID CapturedBaseAddress
;
3350 SIZE_T CapturedBytesToLock
;
3356 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3359 // Invalid set of flags
3361 return STATUS_INVALID_PARAMETER
;
3365 // At least one flag must be specified
3367 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3372 return STATUS_INVALID_PARAMETER
;
3376 // Enter SEH for probing
3381 // Validate output data
3383 ProbeForWritePointer(BaseAddress
);
3384 ProbeForWriteSize_t(NumberOfBytesToLock
);
3389 CapturedBaseAddress
= *BaseAddress
;
3390 CapturedBytesToLock
= *NumberOfBytesToLock
;
3392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3395 // Get exception code
3397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3402 // Catch illegal base address
3404 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3407 // Catch illegal region size
3409 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
3414 return STATUS_INVALID_PARAMETER
;
3418 // 0 is also illegal
3420 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
3423 // Get a reference to the process
3425 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3426 PROCESS_VM_OPERATION
,
3431 if (!NT_SUCCESS(Status
)) return Status
;
3434 // Check if this is is system-mapped
3436 if (MapType
& MAP_SYSTEM
)
3439 // Check for required privilege
3441 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3444 // Fail: Don't have it
3446 ObDereferenceObject(Process
);
3447 return STATUS_PRIVILEGE_NOT_HELD
;
3452 // Check if we should attach
3454 if (CurrentProcess
!= Process
)
3459 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3464 // Call the internal function
3466 Status
= MiLockVirtualMemory(&CapturedBaseAddress
,
3467 &CapturedBytesToLock
,
3473 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3476 // Release reference
3478 ObDereferenceObject(Process
);
3481 // Enter SEH to return data
3486 // Return data to user
3488 *BaseAddress
= CapturedBaseAddress
;
3489 *NumberOfBytesToLock
= CapturedBytesToLock
;
3491 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3494 // Get exception code
3496 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3509 MiUnlockVirtualMemory(
3510 IN OUT PVOID
*BaseAddress
,
3511 IN OUT PSIZE_T RegionSize
,
3514 PEPROCESS CurrentProcess
;
3515 PMMSUPPORT AddressSpace
;
3517 PMMPTE PointerPte
, LastPte
;
3519 #if (_MI_PAGING_LEVELS >= 3)
3522 #if (_MI_PAGING_LEVELS == 4)
3528 /* Lock the address space */
3529 AddressSpace
= MmGetCurrentAddressSpace();
3530 MmLockAddressSpace(AddressSpace
);
3532 /* Make sure we still have an address space */
3533 CurrentProcess
= PsGetCurrentProcess();
3534 if (CurrentProcess
->VmDeleted
)
3536 Status
= STATUS_PROCESS_IS_TERMINATING
;
3540 /* Check the VADs in the requested range */
3541 Status
= MiCheckVadsForLockOperation(BaseAddress
, RegionSize
, &EndAddress
);
3543 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3544 incompatible VAD we continue, like Windows does */
3545 if (Status
== STATUS_ACCESS_VIOLATION
)
3547 Status
= STATUS_NOT_LOCKED
;
3551 /* Get the PTE and PDE */
3552 PointerPte
= MiAddressToPte(*BaseAddress
);
3553 PointerPde
= MiAddressToPde(*BaseAddress
);
3554 #if (_MI_PAGING_LEVELS >= 3)
3555 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3557 #if (_MI_PAGING_LEVELS == 4)
3558 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3561 /* Get the last PTE */
3562 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)EndAddress
- 1));
3564 /* Lock the process working set */
3565 MiLockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3567 /* Loop the pages */
3570 /* Check for a page that is not present */
3572 #if (_MI_PAGING_LEVELS == 4)
3573 (PointerPxe
->u
.Hard
.Valid
== 0) ||
3575 #if (_MI_PAGING_LEVELS >= 3)
3576 (PointerPpe
->u
.Hard
.Valid
== 0) ||
3578 (PointerPde
->u
.Hard
.Valid
== 0) ||
3579 (PointerPte
->u
.Hard
.Valid
== 0))
3581 /* Remember it, but keep going */
3582 Status
= STATUS_NOT_LOCKED
;
3587 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3588 ASSERT(Pfn1
!= NULL
);
3590 /* Check if all of the requested locks are present */
3591 if (((MapType
& MAP_SYSTEM
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_SYSTEM
)) ||
3592 ((MapType
& MAP_PROCESS
) && !MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
)))
3594 /* Remember it, but keep going */
3595 Status
= STATUS_NOT_LOCKED
;
3597 /* Check if no lock is present */
3598 if (!MI_IS_LOCKED_VA(Pfn1
, MAP_PROCESS
| MAP_SYSTEM
))
3600 DPRINT1("FIXME: Should remove the page from WS\n");
3605 /* Go to the next PTE */
3608 /* Check if we're on a PDE boundary */
3609 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3610 #if (_MI_PAGING_LEVELS >= 3)
3611 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3613 #if (_MI_PAGING_LEVELS == 4)
3614 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3616 } while (PointerPte
<= LastPte
);
3618 /* Check if we hit a page that was not locked */
3619 if (Status
== STATUS_NOT_LOCKED
)
3621 goto CleanupWithWsLock
;
3624 /* All pages in the region were locked, so unlock them all */
3626 /* Get the PTE and PDE */
3627 PointerPte
= MiAddressToPte(*BaseAddress
);
3628 PointerPde
= MiAddressToPde(*BaseAddress
);
3629 #if (_MI_PAGING_LEVELS >= 3)
3630 PointerPpe
= MiAddressToPpe(*BaseAddress
);
3632 #if (_MI_PAGING_LEVELS == 4)
3633 PointerPxe
= MiAddressToPxe(*BaseAddress
);
3636 /* Loop the pages */
3640 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
3641 MI_UNLOCK_VA(Pfn1
, MapType
);
3643 /* Go to the next PTE */
3646 /* Check if we're on a PDE boundary */
3647 if (MiIsPteOnPdeBoundary(PointerPte
)) PointerPde
++;
3648 #if (_MI_PAGING_LEVELS >= 3)
3649 if (MiIsPteOnPpeBoundary(PointerPte
)) PointerPpe
++;
3651 #if (_MI_PAGING_LEVELS == 4)
3652 if (MiIsPteOnPxeBoundary(PointerPte
)) PointerPxe
++;
3654 } while (PointerPte
<= LastPte
);
3656 /* Everything is done */
3657 Status
= STATUS_SUCCESS
;
3661 /* Release process working set */
3662 MiUnlockProcessWorkingSet(CurrentProcess
, PsGetCurrentThread());
3665 /* Unlock address space */
3666 MmUnlockAddressSpace(AddressSpace
);
3674 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3675 IN OUT PVOID
*BaseAddress
,
3676 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3680 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3682 BOOLEAN Attached
= FALSE
;
3683 KAPC_STATE ApcState
;
3684 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3685 PVOID CapturedBaseAddress
;
3686 SIZE_T CapturedBytesToUnlock
;
3692 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3695 // Invalid set of flags
3697 return STATUS_INVALID_PARAMETER
;
3701 // At least one flag must be specified
3703 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3708 return STATUS_INVALID_PARAMETER
;
3712 // Enter SEH for probing
3717 // Validate output data
3719 ProbeForWritePointer(BaseAddress
);
3720 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3725 CapturedBaseAddress
= *BaseAddress
;
3726 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3728 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3731 // Get exception code
3733 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3738 // Catch illegal base address
3740 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3743 // Catch illegal region size
3745 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3750 return STATUS_INVALID_PARAMETER
;
3754 // 0 is also illegal
3756 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3759 // Get a reference to the process
3761 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3762 PROCESS_VM_OPERATION
,
3767 if (!NT_SUCCESS(Status
)) return Status
;
3770 // Check if this is is system-mapped
3772 if (MapType
& MAP_SYSTEM
)
3775 // Check for required privilege
3777 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3780 // Fail: Don't have it
3782 ObDereferenceObject(Process
);
3783 return STATUS_PRIVILEGE_NOT_HELD
;
3788 // Check if we should attach
3790 if (CurrentProcess
!= Process
)
3795 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3800 // Call the internal function
3802 Status
= MiUnlockVirtualMemory(&CapturedBaseAddress
,
3803 &CapturedBytesToUnlock
,
3809 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3812 // Release reference
3814 ObDereferenceObject(Process
);
3817 // Enter SEH to return data
3822 // Return data to user
3824 *BaseAddress
= CapturedBaseAddress
;
3825 *NumberOfBytesToUnlock
= CapturedBytesToUnlock
;
3827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3830 // Get exception code
3832 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3839 return STATUS_SUCCESS
;
3844 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3845 IN OUT PVOID
*BaseAddress
,
3846 IN OUT PSIZE_T NumberOfBytesToFlush
,
3847 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3851 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3852 PVOID CapturedBaseAddress
;
3853 SIZE_T CapturedBytesToFlush
;
3854 IO_STATUS_BLOCK LocalStatusBlock
;
3858 // Check if we came from user mode
3860 if (PreviousMode
!= KernelMode
)
3863 // Enter SEH for probing
3868 // Validate all outputs
3870 ProbeForWritePointer(BaseAddress
);
3871 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3872 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3877 CapturedBaseAddress
= *BaseAddress
;
3878 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3880 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3883 // Get exception code
3885 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3894 CapturedBaseAddress
= *BaseAddress
;
3895 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3899 // Catch illegal base address
3901 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3904 // Catch illegal region size
3906 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3911 return STATUS_INVALID_PARAMETER
;
3915 // Get a reference to the process
3917 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3918 PROCESS_VM_OPERATION
,
3923 if (!NT_SUCCESS(Status
)) return Status
;
3928 Status
= MmFlushVirtualMemory(Process
,
3929 &CapturedBaseAddress
,
3930 &CapturedBytesToFlush
,
3934 // Release reference
3936 ObDereferenceObject(Process
);
3939 // Enter SEH to return data
3944 // Return data to user
3946 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3947 *NumberOfBytesToFlush
= 0;
3948 *IoStatusBlock
= LocalStatusBlock
;
3950 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3966 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3968 IN PVOID BaseAddress
,
3969 IN SIZE_T RegionSize
,
3970 IN PVOID
*UserAddressArray
,
3971 OUT PULONG_PTR EntriesInUserAddressArray
,
3972 OUT PULONG Granularity
)
3977 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3978 ULONG_PTR CapturedEntryCount
;
3982 // Check if we came from user mode
3984 if (PreviousMode
!= KernelMode
)
3987 // Enter SEH for probing
3992 // Catch illegal base address
3994 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2
);
3997 // Catch illegal region size
3999 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4004 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3
);
4008 // Validate all data
4010 ProbeForWriteSize_t(EntriesInUserAddressArray
);
4011 ProbeForWriteUlong(Granularity
);
4016 CapturedEntryCount
= *EntriesInUserAddressArray
;
4019 // Must have a count
4021 if (CapturedEntryCount
== 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4024 // Can't be larger than the maximum
4026 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
4031 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5
);
4035 // Probe the actual array
4037 ProbeForWrite(UserAddressArray
,
4038 CapturedEntryCount
* sizeof(PVOID
),
4041 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4044 // Get exception code
4046 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4055 CapturedEntryCount
= *EntriesInUserAddressArray
;
4056 ASSERT(CapturedEntryCount
!= 0);
4060 // Check if this is a local request
4062 if (ProcessHandle
== NtCurrentProcess())
4065 // No need to reference the process
4067 Process
= PsGetCurrentProcess();
4072 // Reference the target
4074 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4075 PROCESS_VM_OPERATION
,
4080 if (!NT_SUCCESS(Status
)) return Status
;
4084 // Compute the last address and validate it
4086 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4087 if (BaseAddress
> EndAddress
)
4092 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4093 return STATUS_INVALID_PARAMETER_4
;
4102 // Dereference if needed
4104 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4107 // Enter SEH to return data
4112 // Return data to user
4114 *EntriesInUserAddressArray
= 0;
4115 *Granularity
= PAGE_SIZE
;
4117 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4120 // Get exception code
4122 Status
= _SEH2_GetExceptionCode();
4129 return STATUS_SUCCESS
;
4137 NtResetWriteWatch(IN HANDLE ProcessHandle
,
4138 IN PVOID BaseAddress
,
4139 IN SIZE_T RegionSize
)
4144 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
4145 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
4148 // Catch illegal base address
4150 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
4153 // Catch illegal region size
4155 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
4160 return STATUS_INVALID_PARAMETER_3
;
4164 // Check if this is a local request
4166 if (ProcessHandle
== NtCurrentProcess())
4169 // No need to reference the process
4171 Process
= PsGetCurrentProcess();
4176 // Reference the target
4178 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4179 PROCESS_VM_OPERATION
,
4184 if (!NT_SUCCESS(Status
)) return Status
;
4188 // Compute the last address and validate it
4190 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
4191 if (BaseAddress
> EndAddress
)
4196 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4197 return STATUS_INVALID_PARAMETER_3
;
4206 // Dereference if needed
4208 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4213 return STATUS_SUCCESS
;
4218 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
4219 IN PVOID BaseAddress
,
4220 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
4221 OUT PVOID MemoryInformation
,
4222 IN SIZE_T MemoryInformationLength
,
4223 OUT PSIZE_T ReturnLength
)
4225 NTSTATUS Status
= STATUS_SUCCESS
;
4226 KPROCESSOR_MODE PreviousMode
;
4228 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
4230 /* Bail out if the address is invalid */
4231 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
4233 /* Probe return buffer */
4234 PreviousMode
= ExGetPreviousMode();
4235 if (PreviousMode
!= KernelMode
)
4239 ProbeForWrite(MemoryInformation
,
4240 MemoryInformationLength
,
4243 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
4245 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4247 Status
= _SEH2_GetExceptionCode();
4251 if (!NT_SUCCESS(Status
))
4257 switch(MemoryInformationClass
)
4259 case MemoryBasicInformation
:
4260 /* Validate the size information of the class */
4261 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
4263 /* The size is invalid */
4264 return STATUS_INFO_LENGTH_MISMATCH
;
4266 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
4269 MemoryInformationLength
,
4273 case MemorySectionName
:
4274 /* Validate the size information of the class */
4275 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
4277 /* The size is invalid */
4278 return STATUS_INFO_LENGTH_MISMATCH
;
4280 Status
= MiQueryMemorySectionName(ProcessHandle
,
4283 MemoryInformationLength
,
4286 case MemoryWorkingSetList
:
4287 case MemoryBasicVlmInformation
:
4289 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
4301 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
4302 IN OUT PVOID
* UBaseAddress
,
4303 IN ULONG_PTR ZeroBits
,
4304 IN OUT PSIZE_T URegionSize
,
4305 IN ULONG AllocationType
,
4309 PMEMORY_AREA MemoryArea
;
4310 PMMVAD Vad
= NULL
, FoundVad
;
4312 PMMSUPPORT AddressSpace
;
4314 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
4315 ULONG_PTR HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
4316 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4317 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4318 PETHREAD CurrentThread
= PsGetCurrentThread();
4319 KAPC_STATE ApcState
;
4320 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
4321 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
4323 PMMPTE PointerPte
, LastPte
;
4325 TABLE_SEARCH_RESULT Result
;
4328 /* Check for valid Zero bits */
4329 if (ZeroBits
> MI_MAX_ZERO_BITS
)
4331 DPRINT1("Too many zero bits\n");
4332 return STATUS_INVALID_PARAMETER_3
;
4335 /* Check for valid Allocation Types */
4336 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
4337 MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_LARGE_PAGES
)))
4339 DPRINT1("Invalid Allocation Type\n");
4340 return STATUS_INVALID_PARAMETER_5
;
4343 /* Check for at least one of these Allocation Types to be set */
4344 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
4346 DPRINT1("No memory allocation base type\n");
4347 return STATUS_INVALID_PARAMETER_5
;
4350 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4351 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
4353 DPRINT1("Invalid use of MEM_RESET\n");
4354 return STATUS_INVALID_PARAMETER_5
;
4357 /* Check if large pages are being used */
4358 if (AllocationType
& MEM_LARGE_PAGES
)
4360 /* Large page allocations MUST be committed */
4361 if (!(AllocationType
& MEM_COMMIT
))
4363 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4364 return STATUS_INVALID_PARAMETER_5
;
4367 /* These flags are not allowed with large page allocations */
4368 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
4370 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4371 return STATUS_INVALID_PARAMETER_5
;
4375 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4376 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
4378 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4379 return STATUS_INVALID_PARAMETER_5
;
4382 /* Check for valid MEM_PHYSICAL usage */
4383 if (AllocationType
& MEM_PHYSICAL
)
4385 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4386 if (!(AllocationType
& MEM_RESERVE
))
4388 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4389 return STATUS_INVALID_PARAMETER_5
;
4392 /* Only these flags are allowed with MEM_PHYSIAL */
4393 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
4395 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4396 return STATUS_INVALID_PARAMETER_5
;
4399 /* Then make sure PAGE_READWRITE is used */
4400 if (Protect
!= PAGE_READWRITE
)
4402 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4403 return STATUS_INVALID_PARAMETER_6
;
4407 /* Calculate the protection mask and make sure it's valid */
4408 ProtectionMask
= MiMakeProtectionMask(Protect
);
4409 if (ProtectionMask
== MM_INVALID_PROTECTION
)
4411 DPRINT1("Invalid protection mask\n");
4412 return STATUS_INVALID_PAGE_PROTECTION
;
4418 /* Check for user-mode parameters */
4419 if (PreviousMode
!= KernelMode
)
4421 /* Make sure they are writable */
4422 ProbeForWritePointer(UBaseAddress
);
4423 ProbeForWriteSize_t(URegionSize
);
4426 /* Capture their values */
4427 PBaseAddress
= *UBaseAddress
;
4428 PRegionSize
= *URegionSize
;
4430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4432 /* Return the exception code */
4433 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4437 /* Make sure the allocation isn't past the VAD area */
4438 if (PBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
4440 DPRINT1("Virtual allocation base above User Space\n");
4441 return STATUS_INVALID_PARAMETER_2
;
4444 /* Make sure the allocation wouldn't overflow past the VAD area */
4445 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4447 DPRINT1("Region size would overflow into kernel-memory\n");
4448 return STATUS_INVALID_PARAMETER_4
;
4451 /* Make sure there's a size specified */
4454 DPRINT1("Region size is invalid (zero)\n");
4455 return STATUS_INVALID_PARAMETER_4
;
4459 // If this is for the current process, just use PsGetCurrentProcess
4461 if (ProcessHandle
== NtCurrentProcess())
4463 Process
= CurrentProcess
;
4468 // Otherwise, reference the process with VM rights and attach to it if
4469 // this isn't the current process. We must attach because we'll be touching
4470 // PTEs and PDEs that belong to user-mode memory, and also touching the
4471 // Working Set which is stored in Hyperspace.
4473 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4474 PROCESS_VM_OPERATION
,
4479 if (!NT_SUCCESS(Status
)) return Status
;
4480 if (CurrentProcess
!= Process
)
4482 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4487 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4488 Process
, PBaseAddress
, ZeroBits
, PRegionSize
, AllocationType
, Protect
);
4491 // Check for large page allocations and make sure that the required privilege
4492 // is being held, before attempting to handle them.
4494 if ((AllocationType
& MEM_LARGE_PAGES
) &&
4495 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
4497 /* Fail without it */
4498 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4499 Status
= STATUS_PRIVILEGE_NOT_HELD
;
4500 goto FailPathNoLock
;
4504 // Fail on the things we don't yet support
4506 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
4508 DPRINT1("MEM_LARGE_PAGES not supported\n");
4509 Status
= STATUS_INVALID_PARAMETER
;
4510 goto FailPathNoLock
;
4512 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
4514 DPRINT1("MEM_PHYSICAL not supported\n");
4515 Status
= STATUS_INVALID_PARAMETER
;
4516 goto FailPathNoLock
;
4518 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
4520 DPRINT1("MEM_WRITE_WATCH not supported\n");
4521 Status
= STATUS_INVALID_PARAMETER
;
4522 goto FailPathNoLock
;
4526 // Check if the caller is reserving memory, or committing memory and letting
4527 // us pick the base address
4529 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
4532 // Do not allow COPY_ON_WRITE through this API
4534 if (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
))
4536 DPRINT1("Copy on write not allowed through this path\n");
4537 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4538 goto FailPathNoLock
;
4542 // Does the caller have an address in mind, or is this a blind commit?
4547 // This is a blind commit, all we need is the region size
4549 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
4551 StartingAddress
= 0;
4554 // Check if ZeroBits were specified
4559 // Calculate the highest address and check if it's valid
4561 HighestAddress
= MAXULONG_PTR
>> ZeroBits
;
4562 if (HighestAddress
> (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
)
4564 Status
= STATUS_INVALID_PARAMETER_3
;
4565 goto FailPathNoLock
;
4572 // This is a reservation, so compute the starting address on the
4573 // expected 64KB granularity, and see where the ending address will
4574 // fall based on the aligned address and the passed in region size
4576 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4577 PRegionSize
= EndingAddress
+ 1 - ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
4578 StartingAddress
= (ULONG_PTR
)PBaseAddress
;
4582 // Allocate and initialize the VAD
4584 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
4587 DPRINT1("Failed to allocate a VAD!\n");
4588 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4589 goto FailPathNoLock
;
4592 RtlZeroMemory(Vad
, sizeof(MMVAD_LONG
));
4593 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
4594 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
4595 Vad
->u
.VadFlags
.PrivateMemory
= 1;
4596 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4601 Status
= MiInsertVadEx(Vad
,
4605 MM_VIRTMEM_GRANULARITY
,
4607 if (!NT_SUCCESS(Status
))
4609 DPRINT1("Failed to insert the VAD!\n");
4610 goto FailPathNoLock
;
4614 // Detach and dereference the target process if
4615 // it was different from the current process
4617 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4618 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4621 // Use SEH to write back the base address and the region size. In the case
4622 // of an exception, we do not return back the exception code, as the memory
4623 // *has* been allocated. The caller would now have to call VirtualQuery
4624 // or do some other similar trick to actually find out where its memory
4625 // allocation ended up
4629 *URegionSize
= PRegionSize
;
4630 *UBaseAddress
= (PVOID
)StartingAddress
;
4632 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4635 // Ignore exception!
4639 DPRINT("Reserved %x bytes at %p.\n", PRegionSize
, StartingAddress
);
4640 return STATUS_SUCCESS
;
4644 // This is a MEM_COMMIT on top of an existing address which must have been
4645 // MEM_RESERVED already. Compute the start and ending base addresses based
4646 // on the user input, and then compute the actual region size once all the
4647 // alignments have been done.
4649 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4650 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4651 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4654 // Lock the address space and make sure the process isn't already dead
4656 AddressSpace
= MmGetCurrentAddressSpace();
4657 MmLockAddressSpace(AddressSpace
);
4658 if (Process
->VmDeleted
)
4660 DPRINT1("Process is dying\n");
4661 Status
= STATUS_PROCESS_IS_TERMINATING
;
4666 // Get the VAD for this address range, and make sure it exists
4668 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4669 EndingAddress
>> PAGE_SHIFT
,
4671 (PMMADDRESS_NODE
*)&FoundVad
);
4672 if (Result
!= TableFoundNode
)
4674 DPRINT1("Could not find a VAD for this allocation\n");
4675 Status
= STATUS_CONFLICTING_ADDRESSES
;
4679 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
4681 /// @todo HACK: pretend success
4682 DPRINT("MEM_RESET not supported\n");
4683 Status
= STATUS_SUCCESS
;
4688 // These kinds of VADs are illegal for this Windows function when trying to
4689 // commit an existing range
4691 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4692 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4693 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4695 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4696 Status
= STATUS_CONFLICTING_ADDRESSES
;
4701 // Make sure that this address range actually fits within the VAD for it
4703 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) ||
4704 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4706 DPRINT1("Address range does not fit into the VAD\n");
4707 Status
= STATUS_CONFLICTING_ADDRESSES
;
4712 // Make sure this is an ARM3 section
4714 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4715 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
4717 DPRINT1("Illegal commit of non-ARM3 section!\n");
4718 Status
= STATUS_ALREADY_COMMITTED
;
4722 // Is this a previously reserved section being committed? If so, enter the
4723 // special section path
4725 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4728 // You cannot commit large page sections through this API
4730 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4732 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4733 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4738 // You can only use caching flags on a rotate VAD
4740 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4741 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4743 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4744 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4749 // We should make sure that the section's permissions aren't being
4752 if (FoundVad
->u
.VadFlags
.NoChange
)
4755 // Make sure it's okay to touch it
4756 // Note: The Windows 2003 kernel has a bug here, passing the
4757 // unaligned base address together with the aligned size,
4758 // potentially covering a region larger than the actual allocation.
4759 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4760 // For now we keep this behavior.
4761 // TODO: analyze possible implications, create test case
4763 Status
= MiCheckSecuredVad(FoundVad
,
4767 if (!NT_SUCCESS(Status
))
4769 DPRINT1("Secured VAD being messed around with\n");
4775 // ARM3 does not support file-backed sections, only shared memory
4777 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4780 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4782 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4783 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4785 DPRINT1("Invalid page protection for rotate VAD\n");
4786 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4791 // Compute PTE addresses and the quota charge, then grab the commit lock
4793 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4794 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4795 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4796 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4799 // Get the segment template PTE and start looping each page
4801 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4802 ASSERT(TempPte
.u
.Long
!= 0);
4803 while (PointerPte
<= LastPte
)
4806 // For each non-already-committed page, write the invalid template PTE
4808 if (PointerPte
->u
.Long
== 0)
4810 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4820 // Now do the commit accounting and release the lock
4822 ASSERT(QuotaCharge
>= QuotaFree
);
4823 QuotaCharge
-= QuotaFree
;
4824 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4825 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4828 // We are done with committing the section pages
4830 Status
= STATUS_SUCCESS
;
4835 // This is a specific ReactOS check because we only use normal VADs
4837 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4840 // While this is an actual Windows check
4842 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4845 // Throw out attempts to use copy-on-write through this API path
4847 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4849 DPRINT1("Write copy attempted when not allowed\n");
4850 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4855 // Initialize a demand-zero PTE
4858 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4859 NT_ASSERT(TempPte
.u
.Long
!= 0);
4862 // Get the PTE, PDE and the last PTE for this address range
4864 PointerPde
= MiAddressToPde(StartingAddress
);
4865 PointerPte
= MiAddressToPte(StartingAddress
);
4866 LastPte
= MiAddressToPte(EndingAddress
);
4869 // Update the commit charge in the VAD as well as in the process, and check
4870 // if this commit charge was now higher than the last recorded peak, in which
4871 // case we also update the peak
4873 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4874 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4875 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4877 Process
->CommitChargePeak
= Process
->CommitCharge
;
4881 // Lock the working set while we play with user pages and page tables
4883 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4886 // Make the current page table valid, and then loop each page within it
4888 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4889 while (PointerPte
<= LastPte
)
4892 // Have we crossed into a new page table?
4894 if (MiIsPteOnPdeBoundary(PointerPte
))
4897 // Get the PDE and now make it valid too
4899 PointerPde
= MiPteToPde(PointerPte
);
4900 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4904 // Is this a zero PTE as expected?
4906 if (PointerPte
->u
.Long
== 0)
4909 // First increment the count of pages in the page table for this
4912 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
4915 // And now write the invalid demand-zero PTE as requested
4917 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4919 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4922 // If the PTE was already decommitted, there is nothing else to do
4923 // but to write the new demand-zero PTE
4925 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4927 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4930 // We don't handle these scenarios yet
4932 if (PointerPte
->u
.Soft
.Valid
== 0)
4934 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4935 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4939 // There's a change in protection, remember this for later, but do
4940 // not yet handle it.
4942 ChangeProtection
= TRUE
;
4946 // Move to the next PTE
4952 // Release the working set lock, unlock the address space, and detach from
4953 // the target process if it was not the current process. Also dereference the
4954 // target process if this wasn't the case.
4956 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
4957 Status
= STATUS_SUCCESS
;
4959 MmUnlockAddressSpace(AddressSpace
);
4961 if (!NT_SUCCESS(Status
))
4965 ExFreePoolWithTag(Vad
, 'SdaV');
4970 // Check if we need to update the protection
4972 if (ChangeProtection
)
4974 PVOID ProtectBaseAddress
= (PVOID
)StartingAddress
;
4975 SIZE_T ProtectSize
= PRegionSize
;
4976 ULONG OldProtection
;
4979 // Change the protection of the region
4981 MiProtectVirtualMemory(Process
,
4982 &ProtectBaseAddress
,
4989 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4990 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4993 // Only write back results on success
4995 if (NT_SUCCESS(Status
))
4998 // Use SEH to write back the base address and the region size. In the case
4999 // of an exception, we strangely do return back the exception code, even
5000 // though the memory *has* been allocated. This mimics Windows behavior and
5001 // there is not much we can do about it.
5005 *URegionSize
= PRegionSize
;
5006 *UBaseAddress
= (PVOID
)StartingAddress
;
5008 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5010 Status
= _SEH2_GetExceptionCode();
5023 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
5024 IN PVOID
* UBaseAddress
,
5025 IN PSIZE_T URegionSize
,
5028 PMEMORY_AREA MemoryArea
;
5031 LONG_PTR AlreadyDecommitted
, CommitReduction
= 0;
5032 ULONG_PTR StartingAddress
, EndingAddress
;
5036 PMMSUPPORT AddressSpace
;
5037 PETHREAD CurrentThread
= PsGetCurrentThread();
5038 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
5039 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
5040 KAPC_STATE ApcState
;
5041 BOOLEAN Attached
= FALSE
;
5045 // Only two flags are supported
5047 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
5049 DPRINT1("Invalid FreeType\n");
5050 return STATUS_INVALID_PARAMETER_4
;
5054 // Check if no flag was used, or if both flags were used
5056 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
5057 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
5059 DPRINT1("Invalid FreeType combination\n");
5060 return STATUS_INVALID_PARAMETER_4
;
5064 // Enter SEH for probe and capture. On failure, return back to the caller
5065 // with an exception violation.
5070 // Check for user-mode parameters and make sure that they are writeable
5072 if (PreviousMode
!= KernelMode
)
5074 ProbeForWritePointer(UBaseAddress
);
5075 ProbeForWriteUlong(URegionSize
);
5079 // Capture the current values
5081 PBaseAddress
= *UBaseAddress
;
5082 PRegionSize
= *URegionSize
;
5084 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5086 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5091 // Make sure the allocation isn't past the user area
5093 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
5095 DPRINT1("Virtual free base above User Space\n");
5096 return STATUS_INVALID_PARAMETER_2
;
5100 // Make sure the allocation wouldn't overflow past the user area
5102 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
5104 DPRINT1("Region size would overflow into kernel-memory\n");
5105 return STATUS_INVALID_PARAMETER_3
;
5109 // If this is for the current process, just use PsGetCurrentProcess
5111 if (ProcessHandle
== NtCurrentProcess())
5113 Process
= CurrentProcess
;
5118 // Otherwise, reference the process with VM rights and attach to it if
5119 // this isn't the current process. We must attach because we'll be touching
5120 // PTEs and PDEs that belong to user-mode memory, and also touching the
5121 // Working Set which is stored in Hyperspace.
5123 Status
= ObReferenceObjectByHandle(ProcessHandle
,
5124 PROCESS_VM_OPERATION
,
5129 if (!NT_SUCCESS(Status
)) return Status
;
5130 if (CurrentProcess
!= Process
)
5132 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
5137 DPRINT("NtFreeVirtualMemory: Process 0x%p, Adress 0x%p, size 0x%x, FreeType %x.\n",
5138 Process
, PBaseAddress
, PRegionSize
, FreeType
);
5141 // Lock the address space
5143 AddressSpace
= MmGetCurrentAddressSpace();
5144 MmLockAddressSpace(AddressSpace
);
5147 // If the address space is being deleted, fail the de-allocation since it's
5148 // too late to do anything about it
5150 if (Process
->VmDeleted
)
5152 DPRINT1("Process is dead\n");
5153 Status
= STATUS_PROCESS_IS_TERMINATING
;
5158 // Compute start and end addresses, and locate the VAD
5160 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
5161 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
5162 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
5165 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
5166 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5171 // If the range exceeds the VAD's ending VPN, fail this request
5173 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
5175 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
5176 Status
= STATUS_UNABLE_TO_FREE_VM
;
5181 // Only private memory (except rotate VADs) can be freed through here */
5183 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
5184 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
5185 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
5187 DPRINT1("Attempt to free section memory\n");
5188 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
5193 // ARM3 does not yet handle protected VM
5195 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
5198 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5199 // and that is is an ARM3 memory area, and not a section view, as we currently
5200 // don't support freeing those though this interface.
5202 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5204 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
5207 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5209 if (FreeType
& MEM_RELEASE
)
5212 // ARM3 only supports this VAD in this path
5214 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
5217 // Is the caller trying to remove the whole VAD, or remove only a portion
5218 // of it? If no region size is specified, then the assumption is that the
5219 // whole VAD is to be destroyed
5224 // The caller must specify the base address identically to the range
5225 // that is stored in the VAD.
5227 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5229 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
5230 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5235 // Now compute the actual start/end addresses based on the VAD
5237 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
5238 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5241 // Finally lock the working set and remove the VAD from the VAD tree
5243 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5244 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5245 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5250 // This means the caller wants to release a specific region within
5251 // the range. We have to find out which range this is -- the following
5252 // possibilities exist plus their union (CASE D):
5254 // STARTING ADDRESS ENDING ADDRESS
5255 // [<========][========================================][=========>]
5256 // CASE A CASE B CASE C
5259 // First, check for case A or D
5261 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
5266 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5269 // This is the easiest one to handle -- it is identical to
5270 // the code path above when the caller sets a zero region size
5271 // and the whole VAD is destroyed
5273 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5274 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
5275 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
5280 // This case is pretty easy too -- we compute a bunch of
5281 // pages to decommit, and then push the VAD's starting address
5282 // a bit further down, then decrement the commit charge
5284 // NOT YET IMPLEMENTED IN ARM3.
5286 DPRINT1("Case A not handled\n");
5287 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5291 // After analyzing the VAD, set it to NULL so that we don't
5292 // free it in the exit path
5300 // This is case B or case C. First check for case C
5302 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
5304 PMEMORY_AREA MemoryArea
;
5307 // This is pretty easy and similar to case A. We compute the
5308 // amount of pages to decommit, update the VAD's commit charge
5309 // and then change the ending address of the VAD to be a bit
5312 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5313 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
5317 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5318 // For ReactOS: shrink the corresponding memory area
5319 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
5320 ASSERT(Vad
->StartingVpn
== MemoryArea
->StartingVpn
);
5321 ASSERT(Vad
->EndingVpn
== MemoryArea
->EndingVpn
);
5322 Vad
->EndingVpn
= (StartingAddress
- 1) >> PAGE_SHIFT
;
5323 MemoryArea
->EndingVpn
= Vad
->EndingVpn
;
5328 // This is case B and the hardest one. Because we are removing
5329 // a chunk of memory from the very middle of the VAD, we must
5330 // actually split the VAD into two new VADs and compute the
5331 // commit charges for each of them, and reinsert new charges.
5333 // NOT YET IMPLEMENTED IN ARM3.
5335 DPRINT1("Case B not handled\n");
5336 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5341 // After analyzing the VAD, set it to NULL so that we don't
5342 // free it in the exit path
5349 // Now we have a range of pages to dereference, so call the right API
5350 // to do that and then release the working set, since we're done messing
5351 // around with process pages.
5353 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
5354 MiUnlockProcessWorkingSetUnsafe(Process
, CurrentThread
);
5355 Status
= STATUS_SUCCESS
;
5359 // Update the process counters
5361 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
5362 Process
->CommitCharge
-= CommitReduction
;
5363 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
5366 // Unlock the address space and free the VAD in failure cases. Next,
5367 // detach from the target process so we can write the region size and the
5368 // base address to the correct source process, and dereference the target
5371 MmUnlockAddressSpace(AddressSpace
);
5372 if (Vad
) ExFreePool(Vad
);
5373 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5374 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5377 // Use SEH to safely return the region size and the base address of the
5378 // deallocation. If we get an access violation, don't return a failure code
5379 // as the deallocation *has* happened. The caller will just have to figure
5380 // out another way to find out where it is (such as VirtualQuery).
5384 *URegionSize
= PRegionSize
;
5385 *UBaseAddress
= (PVOID
)StartingAddress
;
5387 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
5395 // This is the decommit path. You cannot decommit from the following VADs in
5396 // Windows, so fail the vall
5398 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
5399 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
5400 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
5402 DPRINT1("Trying to decommit from invalid VAD\n");
5403 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
5408 // If the caller did not specify a region size, first make sure that this
5409 // region is actually committed. If it is, then compute the ending address
5410 // based on the VAD.
5414 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
5416 DPRINT1("Decomitting non-committed memory\n");
5417 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
5420 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
5424 // Decommit the PTEs for the range plus the actual backing pages for the
5425 // range, then reduce that amount from the commit charge in the VAD
5427 AlreadyDecommitted
= MiDecommitPages((PVOID
)StartingAddress
,
5428 MiAddressToPte(EndingAddress
),
5431 CommitReduction
= MiAddressToPte(EndingAddress
) -
5432 MiAddressToPte(StartingAddress
) +
5436 ASSERT(CommitReduction
>= 0);
5437 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
5438 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
5441 // We are done, go to the exit path without freeing the VAD as it remains
5442 // valid since we have not released the allocation.
5445 Status
= STATUS_SUCCESS
;
5449 // In the failure path, we detach and derefernece the target process, and
5450 // return whatever failure code was sent.
5453 MmUnlockAddressSpace(AddressSpace
);
5454 if (Attached
) KeUnstackDetachProcess(&ApcState
);
5455 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
5462 MmGetPhysicalAddress(PVOID Address
)
5464 PHYSICAL_ADDRESS PhysicalAddress
;
5468 /* Check if the PXE/PPE/PDE is valid */
5470 #if (_MI_PAGING_LEVELS == 4)
5471 (MiAddressToPxe(Address
)->u
.Hard
.Valid
) &&
5473 #if (_MI_PAGING_LEVELS >= 3)
5474 (MiAddressToPpe(Address
)->u
.Hard
.Valid
) &&
5476 (MiAddressToPde(Address
)->u
.Hard
.Valid
))
5478 /* Check for large pages */
5479 TempPde
= *MiAddressToPde(Address
);
5480 if (TempPde
.u
.Hard
.LargePage
)
5482 /* Physical address is base page + large page offset */
5483 PhysicalAddress
.QuadPart
= (ULONG64
)TempPde
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5484 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
* PTE_PER_PAGE
- 1));
5485 return PhysicalAddress
;
5488 /* Check if the PTE is valid */
5489 TempPte
= *MiAddressToPte(Address
);
5490 if (TempPte
.u
.Hard
.Valid
)
5492 /* Physical address is base page + page offset */
5493 PhysicalAddress
.QuadPart
= (ULONG64
)TempPte
.u
.Hard
.PageFrameNumber
<< PAGE_SHIFT
;
5494 PhysicalAddress
.QuadPart
+= ((ULONG_PTR
)Address
& (PAGE_SIZE
- 1));
5495 return PhysicalAddress
;
5499 KeRosDumpStackFrames(NULL
, 20);
5500 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address
);
5501 PhysicalAddress
.QuadPart
= 0;
5502 return PhysicalAddress
;