2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
18 #define MI_MAPPED_COPY_PAGES 14
19 #define MI_POOL_COPY_BYTES 512
20 #define MI_MAX_TRANSFER_SIZE 64 * 1024
23 MiProtectVirtualMemory(IN PEPROCESS Process
,
24 IN OUT PVOID
*BaseAddress
,
25 IN OUT PSIZE_T NumberOfBytesToProtect
,
26 IN ULONG NewAccessProtection
,
27 OUT PULONG OldAccessProtection OPTIONAL
);
31 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
33 IN ULONG ProtectionMask
,
35 IN BOOLEAN CaptureDirtyBit
);
38 /* PRIVATE FUNCTIONS **********************************************************/
42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
43 IN ULONG_PTR EndingAddress
,
47 PMMPTE PointerPte
, LastPte
, PointerPde
;
50 /* Compute starting and ending PTE and PDE addresses */
51 PointerPde
= MiAddressToPde(StartingAddress
);
52 PointerPte
= MiAddressToPte(StartingAddress
);
53 LastPte
= MiAddressToPte(EndingAddress
);
55 /* Handle commited pages first */
56 if (Vad
->u
.VadFlags
.MemCommit
== 1)
58 /* This is a committed VAD, so Assume the whole range is committed */
59 CommittedPages
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
61 /* Is the PDE demand-zero? */
62 PointerPde
= MiAddressToPte(PointerPte
);
63 if (PointerPde
->u
.Long
!= 0)
65 /* It is not. Is it valid? */
66 if (PointerPde
->u
.Hard
.Valid
== 0)
69 PointerPte
= MiPteToAddress(PointerPde
);
70 MiMakeSystemAddressValid(PointerPte
, Process
);
75 /* It is, skip it and move to the next PDE, unless we're done */
77 PointerPte
= MiPteToAddress(PointerPde
);
78 if (PointerPte
> LastPte
) return CommittedPages
;
81 /* Now loop all the PTEs in the range */
82 while (PointerPte
<= LastPte
)
84 /* Have we crossed a PDE boundary? */
85 if (MiIsPteOnPdeBoundary(PointerPte
))
87 /* Is this PDE demand zero? */
88 PointerPde
= MiAddressToPte(PointerPte
);
89 if (PointerPde
->u
.Long
!= 0)
91 /* It isn't -- is it valid? */
92 if (PointerPde
->u
.Hard
.Valid
== 0)
94 /* Nope, fault it in */
95 PointerPte
= MiPteToAddress(PointerPde
);
96 MiMakeSystemAddressValid(PointerPte
, Process
);
101 /* It is, skip it and move to the next PDE */
103 PointerPte
= MiPteToAddress(PointerPde
);
108 /* Is this PTE demand zero? */
109 if (PointerPte
->u
.Long
!= 0)
111 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
112 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
113 (PointerPte
->u
.Hard
.Valid
== 0) &&
114 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
115 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
117 /* It is, so remove it from the count of commited pages */
122 /* Move to the next PTE */
126 /* Return how many committed pages there still are */
127 return CommittedPages
;
130 /* This is a non-commited VAD, so assume none of it is committed */
133 /* Is the PDE demand-zero? */
134 PointerPde
= MiAddressToPte(PointerPte
);
135 if (PointerPde
->u
.Long
!= 0)
137 /* It isn't -- is it invalid? */
138 if (PointerPde
->u
.Hard
.Valid
== 0)
140 /* It is, so page it in */
141 PointerPte
= MiPteToAddress(PointerPde
);
142 MiMakeSystemAddressValid(PointerPte
, Process
);
147 /* It is, so skip it and move to the next PDE */
149 PointerPte
= MiPteToAddress(PointerPde
);
150 if (PointerPte
> LastPte
) return CommittedPages
;
153 /* Loop all the PTEs in this PDE */
154 while (PointerPte
<= LastPte
)
156 /* Have we crossed a PDE boundary? */
157 if (MiIsPteOnPdeBoundary(PointerPte
))
159 /* Is this new PDE demand-zero? */
160 PointerPde
= MiAddressToPte(PointerPte
);
161 if (PointerPde
->u
.Long
!= 0)
163 /* It isn't. Is it valid? */
164 if (PointerPde
->u
.Hard
.Valid
== 0)
166 /* It isn't, so make it valid */
167 PointerPte
= MiPteToAddress(PointerPde
);
168 MiMakeSystemAddressValid(PointerPte
, Process
);
173 /* It is, so skip it and move to the next one */
175 PointerPte
= MiPteToAddress(PointerPde
);
180 /* Is this PTE demand-zero? */
181 if (PointerPte
->u
.Long
!= 0)
183 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
184 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
185 (PointerPte
->u
.Hard
.Valid
== 1) ||
186 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
187 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
189 /* It is! So we'll treat this as a committed page */
194 /* Move to the next PTE */
198 /* Return how many committed pages we found in this VAD */
199 return CommittedPages
;
204 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
205 IN PEPROCESS CurrentProcess
)
208 BOOLEAN WsWasLocked
= FALSE
, LockChange
= FALSE
;
209 PETHREAD CurrentThread
= PsGetCurrentThread();
211 /* Must be a non-pool page table, since those are double-mapped already */
212 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
213 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
214 (PageTableVirtualAddress
> MmPagedPoolEnd
));
216 /* Working set lock or PFN lock should be held */
217 ASSERT(KeAreAllApcsDisabled() == TRUE
);
219 /* Check if the page table is valid */
220 while (!MmIsAddressValid(PageTableVirtualAddress
))
222 /* Check if the WS is locked */
223 if (CurrentThread
->OwnsProcessWorkingSetExclusive
)
225 /* Unlock the working set and remember it was locked */
226 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
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 if (WsWasLocked
) MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
245 /* This flag will be useful later when we do better locking */
249 /* Let caller know what the lock state is */
255 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
259 BOOLEAN LockChange
= FALSE
;
261 /* Must be e kernel address */
262 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
264 /* Check if the page is valid */
265 while (!MmIsAddressValid(VirtualAddress
))
267 /* Release the PFN database */
268 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
271 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
272 if (!NT_SUCCESS(Status
))
274 /* This should not fail */
275 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
279 (ULONG_PTR
)VirtualAddress
);
282 /* This flag will be useful later when we do better locking */
285 /* Lock the PFN database */
286 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
289 /* Let caller know what the lock state is */
295 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
296 IN PFN_NUMBER PageCount
,
298 OUT PPFN_NUMBER ValidPages
)
300 PFN_COUNT ActualPages
= 0;
301 PETHREAD CurrentThread
= PsGetCurrentThread();
303 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
305 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
307 /* Lock the system working set */
308 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
313 /* Make sure there's some data about the page */
314 if (PointerPte
->u
.Long
)
316 /* As always, only handle current ARM3 scenarios */
317 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
318 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
320 /* Normally this is one possibility -- freeing a valid page */
321 if (PointerPte
->u
.Hard
.Valid
)
323 /* Get the page PFN */
324 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
325 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
327 /* Should not have any working set data yet */
328 ASSERT(Pfn1
->u1
.WsIndex
== 0);
330 /* Actual valid, legitimate, pages */
331 if (ValidPages
) (*ValidPages
)++;
333 /* Get the page table entry */
334 PageTableIndex
= Pfn1
->u4
.PteFrame
;
335 Pfn2
= MiGetPfnEntry(PageTableIndex
);
337 /* Lock the PFN database */
338 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
340 /* Delete it the page */
341 MI_SET_PFN_DELETED(Pfn1
);
342 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
344 /* Decrement the page table too */
345 MiDecrementShareCount(Pfn2
, PageTableIndex
);
347 /* Release the PFN database */
348 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
350 /* Destroy the PTE */
351 PointerPte
->u
.Long
= 0;
354 /* Actual legitimate pages */
360 * The only other ARM3 possibility is a demand zero page, which would
361 * mean freeing some of the paged pool pages that haven't even been
362 * touched yet, as part of a larger allocation.
364 * Right now, we shouldn't expect any page file information in the PTE
366 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
368 /* Destroy the PTE */
369 PointerPte
->u
.Long
= 0;
377 /* Release the working set */
378 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
380 /* Flush the entire TLB */
381 KeFlushEntireTb(TRUE
, TRUE
);
389 MiDeletePte(IN PMMPTE PointerPte
,
390 IN PVOID VirtualAddress
,
391 IN PEPROCESS CurrentProcess
,
392 IN PMMPTE PrototypePte
)
396 PFN_NUMBER PageFrameIndex
;
399 /* PFN lock must be held */
400 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
402 /* Capture the PTE */
403 TempPte
= *PointerPte
;
405 /* We only support valid PTEs for now */
406 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
407 if (TempPte
.u
.Hard
.Valid
== 0)
409 /* Invalid PTEs not supported yet */
410 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
411 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
414 /* Get the PFN entry */
415 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
416 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
418 /* Check if this is a valid, prototype PTE */
419 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
421 /* Get the PDE and make sure it's faulted in */
422 PointerPde
= MiPteToPde(PointerPte
);
423 if (PointerPde
->u
.Hard
.Valid
== 0)
425 #if (_MI_PAGING_LEVELS == 2)
426 /* Could be paged pool access from a new process -- synchronize the page directories */
427 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
430 /* The PDE must be valid at this point */
431 KeBugCheckEx(MEMORY_MANAGEMENT
,
433 (ULONG_PTR
)PointerPte
,
435 (ULONG_PTR
)VirtualAddress
);
437 #if (_MI_PAGING_LEVELS == 2)
440 /* Drop the share count */
441 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
443 /* Either a fork, or this is the shared user data page */
444 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
446 /* If it's not the shared user page, then crash, since there's no fork() yet */
447 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
448 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
450 /* Must be some sort of memory corruption */
451 KeBugCheckEx(MEMORY_MANAGEMENT
,
453 (ULONG_PTR
)PointerPte
,
454 (ULONG_PTR
)PrototypePte
,
455 (ULONG_PTR
)Pfn1
->PteAddress
);
461 /* Make sure the saved PTE address is valid */
462 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
464 /* The PFN entry is illegal, or invalid */
465 KeBugCheckEx(MEMORY_MANAGEMENT
,
467 (ULONG_PTR
)PointerPte
,
469 (ULONG_PTR
)Pfn1
->PteAddress
);
472 /* There should only be 1 shared reference count */
473 ASSERT(Pfn1
->u2
.ShareCount
== 1);
475 /* Drop the reference on the page table. */
476 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
478 /* Mark the PFN for deletion and dereference what should be the last ref */
479 MI_SET_PFN_DELETED(Pfn1
);
480 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
482 /* We should eventually do this */
483 //CurrentProcess->NumberOfPrivatePages--;
486 /* Destroy the PTE and flush the TLB */
487 PointerPte
->u
.Long
= 0;
493 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
494 IN ULONG_PTR EndingAddress
,
497 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
500 PEPROCESS CurrentProcess
;
502 BOOLEAN AddressGap
= FALSE
;
503 PSUBSECTION Subsection
;
504 PUSHORT UsedPageTableEntries
;
506 /* Get out if this is a fake VAD, RosMm will free the marea pages */
507 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
509 /* Grab the process and PTE/PDE for the address being deleted */
510 CurrentProcess
= PsGetCurrentProcess();
511 PointerPde
= MiAddressToPde(Va
);
512 PointerPte
= MiAddressToPte(Va
);
514 /* Check if this is a section VAD or a VM VAD */
515 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
517 /* Don't worry about prototypes */
518 PrototypePte
= LastPrototypePte
= NULL
;
522 /* Get the prototype PTE */
523 PrototypePte
= Vad
->FirstPrototypePte
;
524 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
527 /* In all cases, we don't support fork() yet */
528 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
530 /* Loop the PTE for each VA */
533 /* First keep going until we find a valid PDE */
534 while (!PointerPde
->u
.Long
)
536 /* There are gaps in the address space */
539 /* Still no valid PDE, try the next 4MB (or whatever) */
542 /* Update the PTE on this new boundary */
543 PointerPte
= MiPteToAddress(PointerPde
);
545 /* Check if all the PDEs are invalid, so there's nothing to free */
546 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
547 if (Va
> EndingAddress
) return;
550 /* Now check if the PDE is mapped in */
551 if (!PointerPde
->u
.Hard
.Valid
)
553 /* It isn't, so map it in */
554 PointerPte
= MiPteToAddress(PointerPde
);
555 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
558 /* Now we should have a valid PDE, mapped in, and still have some VA */
559 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
560 ASSERT(Va
<= EndingAddress
);
561 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap
) && (LastPrototypePte
))
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
574 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
578 /* No more subsections, we are done with prototype PTEs */
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
587 /* Capture the PDE and make sure it exists */
588 TempPte
= *PointerPte
;
591 DPRINT("Decrement used PTEs by address: %lx\n", Va
);
592 (*UsedPageTableEntries
)--;
593 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
594 DPRINT("Refs: %lx\n", (*UsedPageTableEntries
));
596 /* Check if the PTE is actually mapped in */
597 if (TempPte
.u
.Long
& 0xFFFFFC01)
599 /* Are we dealing with section VAD? */
600 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
602 /* We need to skip to the next correct prototype PTE */
603 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
605 /* And we need the subsection to skip to the next last prototype PTE */
606 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
610 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
614 /* No more subsections, we are done with prototype PTEs */
619 /* Check for prototype PTE */
620 if ((TempPte
.u
.Hard
.Valid
== 0) &&
621 (TempPte
.u
.Soft
.Prototype
== 1))
624 PointerPte
->u
.Long
= 0;
628 /* Delete the PTE proper */
629 MiDeletePte(PointerPte
,
637 /* The PTE was never mapped, just nuke it here */
638 PointerPte
->u
.Long
= 0;
642 /* Update the address and PTE for it */
647 /* Making sure the PDE is still valid */
648 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
650 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
652 /* The PDE should still be valid at this point */
653 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
655 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va
, PointerPde
->u
.Hard
.PageFrameNumber
);
656 if (!(*UsedPageTableEntries
))
658 DPRINT("They are!\n");
659 if (PointerPde
->u
.Long
!= 0)
661 DPRINT("PDE active: %lx in %16s\n", PointerPde
->u
.Hard
.PageFrameNumber
, CurrentProcess
->ImageFileName
);
663 /* Delete the PTE proper */
664 MiDeletePte(PointerPde
,
665 MiPteToAddress(PointerPde
),
671 /* Release the lock and get out if we're done */
672 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
673 if (Va
> EndingAddress
) return;
675 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
676 PointerPde
= MiAddressToPde(Va
);
682 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
683 OUT PBOOLEAN HaveBadAddress
,
684 OUT PULONG_PTR BadAddress
)
686 PEXCEPTION_RECORD ExceptionRecord
;
692 *HaveBadAddress
= FALSE
;
695 // Get the exception record
697 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
700 // Look at the exception code
702 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
703 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
704 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
707 // We can tell the address if we have more than one parameter
709 if (ExceptionRecord
->NumberParameters
> 1)
712 // Return the address
714 *HaveBadAddress
= TRUE
;
715 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
720 // Continue executing the next handler
722 return EXCEPTION_EXECUTE_HANDLER
;
727 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
728 IN PVOID SourceAddress
,
729 IN PEPROCESS TargetProcess
,
730 OUT PVOID TargetAddress
,
731 IN SIZE_T BufferSize
,
732 IN KPROCESSOR_MODE PreviousMode
,
733 OUT PSIZE_T ReturnSize
)
735 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
736 PMDL Mdl
= (PMDL
)MdlBuffer
;
737 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
738 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
739 volatile BOOLEAN PagesLocked
;
740 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
741 volatile PVOID MdlAddress
;
743 BOOLEAN HaveBadAddress
;
744 ULONG_PTR BadAddress
;
745 NTSTATUS Status
= STATUS_SUCCESS
;
749 // Calculate the maximum amount of data to move
751 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
752 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
753 CurrentSize
= TotalSize
;
754 RemainingSize
= BufferSize
;
757 // Loop as long as there is still data
759 while (RemainingSize
> 0)
762 // Check if this transfer will finish everything off
764 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
767 // Attach to the source address space
769 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
772 // Reset state for this pass
776 FailedInMoving
= FALSE
;
777 ASSERT(FailedInProbe
== FALSE
);
780 // Protect user-mode copy
785 // If this is our first time, probe the buffer
787 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
790 // Catch a failure here
792 FailedInProbe
= TRUE
;
797 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
802 FailedInProbe
= FALSE
;
806 // Initialize and probe and lock the MDL
808 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
809 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
815 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
824 // Use our SEH handler to pick this up
826 FailedInMapping
= TRUE
;
827 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
831 // Now let go of the source and grab to the target process
833 KeUnstackDetachProcess(&ApcState
);
834 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
837 // Check if this is our first time through
839 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
842 // Catch a failure here
844 FailedInProbe
= TRUE
;
849 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
854 FailedInProbe
= FALSE
;
858 // Now do the actual move
860 FailedInMoving
= TRUE
;
861 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
863 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
868 // Detach from whoever we may be attached to
870 KeUnstackDetachProcess(&ApcState
);
873 // Check if we had mapped the pages
875 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
878 // Check if we had locked the pages
880 if (PagesLocked
) MmUnlockPages(Mdl
);
883 // Check if we hit working set quota
885 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
890 return STATUS_WORKING_SET_QUOTA
;
894 // Check if we failed during the probe or mapping
896 if ((FailedInProbe
) || (FailedInMapping
))
901 Status
= _SEH2_GetExceptionCode();
902 _SEH2_YIELD(return Status
);
906 // Otherwise, we failed probably during the move
908 *ReturnSize
= BufferSize
- RemainingSize
;
912 // Check if we know exactly where we stopped copying
917 // Return the exact number of bytes copied
919 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
924 // Return partial copy
926 Status
= STATUS_PARTIAL_COPY
;
931 // Check for SEH status
933 if (Status
!= STATUS_SUCCESS
) return Status
;
936 // Detach from target
938 KeUnstackDetachProcess(&ApcState
);
943 MmUnmapLockedPages(MdlAddress
, Mdl
);
947 // Update location and size
949 RemainingSize
-= CurrentSize
;
950 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
951 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
957 *ReturnSize
= BufferSize
;
958 return STATUS_SUCCESS
;
963 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
964 IN PVOID SourceAddress
,
965 IN PEPROCESS TargetProcess
,
966 OUT PVOID TargetAddress
,
967 IN SIZE_T BufferSize
,
968 IN KPROCESSOR_MODE PreviousMode
,
969 OUT PSIZE_T ReturnSize
)
971 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
972 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
973 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
974 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
977 BOOLEAN HaveBadAddress
;
978 ULONG_PTR BadAddress
;
979 NTSTATUS Status
= STATUS_SUCCESS
;
983 // Calculate the maximum amount of data to move
985 TotalSize
= MI_MAX_TRANSFER_SIZE
;
986 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
987 CurrentSize
= TotalSize
;
988 RemainingSize
= BufferSize
;
991 // Check if we can use the stack
993 if (BufferSize
<= MI_POOL_COPY_BYTES
)
998 PoolAddress
= (PVOID
)StackBuffer
;
1005 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
1006 if (!PoolAddress
) ASSERT(FALSE
);
1007 HavePoolAddress
= TRUE
;
1011 // Loop as long as there is still data
1013 while (RemainingSize
> 0)
1016 // Check if this transfer will finish everything off
1018 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1021 // Attach to the source address space
1023 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1026 // Reset state for this pass
1028 FailedInMoving
= FALSE
;
1029 ASSERT(FailedInProbe
== FALSE
);
1032 // Protect user-mode copy
1037 // If this is our first time, probe the buffer
1039 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1042 // Catch a failure here
1044 FailedInProbe
= TRUE
;
1049 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1054 FailedInProbe
= FALSE
;
1060 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1063 // Now let go of the source and grab to the target process
1065 KeUnstackDetachProcess(&ApcState
);
1066 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1069 // Check if this is our first time through
1071 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1074 // Catch a failure here
1076 FailedInProbe
= TRUE
;
1081 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1086 FailedInProbe
= FALSE
;
1090 // Now do the actual move
1092 FailedInMoving
= TRUE
;
1093 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1095 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1100 // Detach from whoever we may be attached to
1102 KeUnstackDetachProcess(&ApcState
);
1105 // Check if we had allocated pool
1107 if (HavePoolAddress
) ExFreePool(PoolAddress
);
1110 // Check if we failed during the probe
1117 Status
= _SEH2_GetExceptionCode();
1118 _SEH2_YIELD(return Status
);
1122 // Otherwise, we failed, probably during the move
1124 *ReturnSize
= BufferSize
- RemainingSize
;
1128 // Check if we know exactly where we stopped copying
1133 // Return the exact number of bytes copied
1135 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1140 // Return partial copy
1142 Status
= STATUS_PARTIAL_COPY
;
1147 // Check for SEH status
1149 if (Status
!= STATUS_SUCCESS
) return Status
;
1152 // Detach from target
1154 KeUnstackDetachProcess(&ApcState
);
1157 // Update location and size
1159 RemainingSize
-= CurrentSize
;
1160 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1161 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1166 // Check if we had allocated pool
1168 if (HavePoolAddress
) ExFreePool(PoolAddress
);
1173 *ReturnSize
= BufferSize
;
1174 return STATUS_SUCCESS
;
1179 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1180 IN PVOID SourceAddress
,
1181 IN PEPROCESS TargetProcess
,
1182 OUT PVOID TargetAddress
,
1183 IN SIZE_T BufferSize
,
1184 IN KPROCESSOR_MODE PreviousMode
,
1185 OUT PSIZE_T ReturnSize
)
1188 PEPROCESS Process
= SourceProcess
;
1191 // Don't accept zero-sized buffers
1193 if (!BufferSize
) return STATUS_SUCCESS
;
1196 // If we are copying from ourselves, lock the target instead
1198 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1201 // Acquire rundown protection
1203 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1208 return STATUS_PROCESS_IS_TERMINATING
;
1212 // See if we should use the pool copy
1214 if (BufferSize
> MI_POOL_COPY_BYTES
)
1219 Status
= MiDoMappedCopy(SourceProcess
,
1232 Status
= MiDoPoolCopy(SourceProcess
,
1244 ExReleaseRundownProtection(&Process
->RundownProtect
);
1250 MmFlushVirtualMemory(IN PEPROCESS Process
,
1251 IN OUT PVOID
*BaseAddress
,
1252 IN OUT PSIZE_T RegionSize
,
1253 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1261 return STATUS_SUCCESS
;
1266 MiGetPageProtection(IN PMMPTE PointerPte
)
1272 /* Copy this PTE's contents */
1273 TempPte
= *PointerPte
;
1275 /* Assure it's not totally zero */
1276 ASSERT(TempPte
.u
.Long
);
1278 /* Check for a special prototype format */
1279 if (TempPte
.u
.Soft
.Valid
== 0 &&
1280 TempPte
.u
.Soft
.Prototype
== 1)
1282 /* Unsupported now */
1287 /* In the easy case of transition or demand zero PTE just return its protection */
1288 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1290 /* If we get here, the PTE is valid, so look up the page in PFN database */
1291 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1292 if (!Pfn
->u3
.e1
.PrototypePte
)
1294 /* Return protection of the original pte */
1295 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1296 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1299 /* This is software PTE */
1300 DPRINT1("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1301 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte
));
1302 DPRINT1("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1303 DPRINT1("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1304 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1309 MiQueryAddressState(IN PVOID Va
,
1311 IN PEPROCESS TargetProcess
,
1312 OUT PULONG ReturnedProtect
,
1316 PMMPTE PointerPte
, ProtoPte
;
1318 MMPTE TempPte
, TempProtoPte
;
1319 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1320 ULONG State
= MEM_RESERVE
, Protect
= 0;
1321 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1322 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1324 /* Only normal VADs supported */
1325 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1327 /* Get the PDE and PTE for the address */
1328 PointerPde
= MiAddressToPde(Va
);
1329 PointerPte
= MiAddressToPte(Va
);
1331 /* Return the next range */
1332 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1334 /* Is the PDE demand-zero? */
1335 if (PointerPde
->u
.Long
!= 0)
1337 /* It is not. Is it valid? */
1338 if (PointerPde
->u
.Hard
.Valid
== 0)
1340 /* Is isn't, fault it in */
1341 PointerPte
= MiPteToAddress(PointerPde
);
1342 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1348 /* It is, skip it and move to the next PDE */
1349 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1352 /* Is it safe to try reading the PTE? */
1355 /* FIXME: watch out for large pages */
1356 ASSERT(PointerPde
->u
.Hard
.LargePage
== FALSE
);
1358 /* Capture the PTE */
1359 TempPte
= *PointerPte
;
1360 if (TempPte
.u
.Long
!= 0)
1362 /* The PTE is valid, so it's not zeroed out */
1363 DemandZeroPte
= FALSE
;
1365 /* Is it a decommited, invalid, or faulted PTE? */
1366 if ((TempPte
.u
.Soft
.Protection
== MM_DECOMMIT
) &&
1367 (TempPte
.u
.Hard
.Valid
== 0) &&
1368 ((TempPte
.u
.Soft
.Prototype
== 0) ||
1369 (TempPte
.u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1371 /* Otherwise our defaults should hold */
1372 ASSERT(Protect
== 0);
1373 ASSERT(State
== MEM_RESERVE
);
1377 /* This means it's committed */
1380 /* We don't support these */
1381 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1382 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1383 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1385 /* Get protection state of this page */
1386 Protect
= MiGetPageProtection(PointerPte
);
1388 /* Check if this is an image-backed VAD */
1389 if ((TempPte
.u
.Soft
.Valid
== 0) &&
1390 (TempPte
.u
.Soft
.Prototype
== 1) &&
1391 (Vad
->u
.VadFlags
.PrivateMemory
== 0) &&
1394 DPRINT1("Not supported\n");
1401 /* Check if this was a demand-zero PTE, since we need to find the state */
1404 /* Not yet handled */
1405 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadDevicePhysicalMemory
);
1406 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadAwe
);
1408 /* Check if this is private commited memory, or an section-backed VAD */
1409 if ((Vad
->u
.VadFlags
.PrivateMemory
== 0) && (Vad
->ControlArea
))
1411 /* Tell caller about the next range */
1412 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1414 /* Get the prototype PTE for this VAD */
1415 ProtoPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
,
1416 (ULONG_PTR
)Va
>> PAGE_SHIFT
);
1419 /* We should unlock the working set, but it's not being held! */
1421 /* Is the prototype PTE actually valid (committed)? */
1422 TempProtoPte
= *ProtoPte
;
1423 if (TempProtoPte
.u
.Long
)
1425 /* Unless this is a memory-mapped file, handle it like private VAD */
1427 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadImageMap
);
1428 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1431 /* We should re-lock the working set */
1434 else if (Vad
->u
.VadFlags
.MemCommit
)
1436 /* This is committed memory */
1439 /* Convert the protection */
1440 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1444 /* Return the protection code */
1445 *ReturnedProtect
= Protect
;
1451 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1452 IN PVOID BaseAddress
,
1453 OUT PVOID MemoryInformation
,
1454 IN SIZE_T MemoryInformationLength
,
1455 OUT PSIZE_T ReturnLength
)
1457 PEPROCESS TargetProcess
;
1458 NTSTATUS Status
= STATUS_SUCCESS
;
1460 PVOID Address
, NextAddress
;
1461 BOOLEAN Found
= FALSE
;
1462 ULONG NewProtect
, NewState
;
1464 MEMORY_BASIC_INFORMATION MemoryInfo
;
1465 KAPC_STATE ApcState
;
1466 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1467 PMEMORY_AREA MemoryArea
;
1468 SIZE_T ResultLength
;
1470 /* Check for illegal addresses in user-space, or the shared memory area */
1471 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1472 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1474 Address
= PAGE_ALIGN(BaseAddress
);
1476 /* Make up an info structure describing this range */
1477 MemoryInfo
.BaseAddress
= Address
;
1478 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1479 MemoryInfo
.Type
= MEM_PRIVATE
;
1481 /* Special case for shared data */
1482 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1484 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1485 MemoryInfo
.State
= MEM_COMMIT
;
1486 MemoryInfo
.Protect
= PAGE_READONLY
;
1487 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1491 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1492 MemoryInfo
.State
= MEM_RESERVE
;
1493 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1494 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1497 /* Return the data, NtQueryInformation already probed it*/
1498 if (PreviousMode
!= KernelMode
)
1502 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1503 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1505 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1507 Status
= _SEH2_GetExceptionCode();
1513 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1514 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1520 /* Check if this is for a local or remote process */
1521 if (ProcessHandle
== NtCurrentProcess())
1523 TargetProcess
= PsGetCurrentProcess();
1527 /* Reference the target process */
1528 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1529 PROCESS_QUERY_INFORMATION
,
1531 ExGetPreviousMode(),
1532 (PVOID
*)&TargetProcess
,
1534 if (!NT_SUCCESS(Status
)) return Status
;
1536 /* Attach to it now */
1537 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1541 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1542 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1544 /* Scan on the right */
1545 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1546 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1549 /* Check if this VAD covers the allocation range */
1550 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1551 (BaseVpn
<= Vad
->EndingVpn
))
1558 /* Check if this VAD is too high */
1559 if (BaseVpn
< Vad
->StartingVpn
)
1561 /* Stop if there is no left child */
1562 if (!Vad
->LeftChild
) break;
1564 /* Search on the left next */
1565 Vad
= Vad
->LeftChild
;
1569 /* Then this VAD is too low, keep searching on the right */
1570 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1572 /* Stop if there is no right child */
1573 if (!Vad
->RightChild
) break;
1575 /* Search on the right next */
1576 Vad
= Vad
->RightChild
;
1581 /* Was a VAD found? */
1584 Address
= PAGE_ALIGN(BaseAddress
);
1586 /* Calculate region size */
1589 if (Vad
->StartingVpn
>= BaseVpn
)
1591 /* Region size is the free space till the start of that VAD */
1592 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1596 /* Get the next VAD */
1597 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1600 /* Region size is the free space till the start of that VAD */
1601 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1605 /* Maximum possible region size with that base address */
1606 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1612 /* Maximum possible region size with that base address */
1613 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1616 /* Check if we were attached */
1617 if (ProcessHandle
!= NtCurrentProcess())
1619 /* Detach and derefernece the process */
1620 KeUnstackDetachProcess(&ApcState
);
1621 ObDereferenceObject(TargetProcess
);
1624 /* Build the rest of the initial information block */
1625 MemoryInfo
.BaseAddress
= Address
;
1626 MemoryInfo
.AllocationBase
= NULL
;
1627 MemoryInfo
.AllocationProtect
= 0;
1628 MemoryInfo
.State
= MEM_FREE
;
1629 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1630 MemoryInfo
.Type
= 0;
1632 /* Return the data, NtQueryInformation already probed it*/
1633 if (PreviousMode
!= KernelMode
)
1637 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1638 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1640 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1642 Status
= _SEH2_GetExceptionCode();
1648 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1649 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1655 /* Set the correct memory type based on what kind of VAD this is */
1656 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1657 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1659 MemoryInfo
.Type
= MEM_PRIVATE
;
1661 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1663 MemoryInfo
.Type
= MEM_IMAGE
;
1667 MemoryInfo
.Type
= MEM_MAPPED
;
1670 /* Lock the address space of the process */
1671 MmLockAddressSpace(&TargetProcess
->Vm
);
1673 /* Find the memory area the specified address belongs to */
1674 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1675 ASSERT(MemoryArea
!= NULL
);
1677 /* Determine information dependent on the memory area type */
1678 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1680 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1681 ASSERT(NT_SUCCESS(Status
));
1685 /* Build the initial information block */
1686 Address
= PAGE_ALIGN(BaseAddress
);
1687 MemoryInfo
.BaseAddress
= Address
;
1688 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1689 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1690 MemoryInfo
.Type
= MEM_PRIVATE
;
1692 /* Find the largest chunk of memory which has the same state and protection mask */
1693 MemoryInfo
.State
= MiQueryAddressState(Address
,
1696 &MemoryInfo
.Protect
,
1698 Address
= NextAddress
;
1699 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1701 /* Keep going unless the state or protection mask changed */
1702 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1703 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1704 Address
= NextAddress
;
1707 /* Now that we know the last VA address, calculate the region size */
1708 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1711 /* Unlock the address space of the process */
1712 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1714 /* Check if we were attached */
1715 if (ProcessHandle
!= NtCurrentProcess())
1717 /* Detach and derefernece the process */
1718 KeUnstackDetachProcess(&ApcState
);
1719 ObDereferenceObject(TargetProcess
);
1722 /* Return the data, NtQueryInformation already probed it*/
1723 if (PreviousMode
!= KernelMode
)
1727 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1728 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1730 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1732 Status
= _SEH2_GetExceptionCode();
1738 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1739 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1743 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1744 "State: %lx Type: %lx Size: %lx\n",
1745 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1746 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1747 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1754 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress
,
1755 IN ULONG_PTR EndingAddress
,
1757 IN PEPROCESS Process
)
1759 PMMPTE PointerPte
, LastPte
, PointerPde
;
1760 BOOLEAN OnBoundary
= TRUE
;
1763 /* Get the PDE and PTE addresses */
1764 PointerPde
= MiAddressToPde(StartingAddress
);
1765 PointerPte
= MiAddressToPte(StartingAddress
);
1766 LastPte
= MiAddressToPte(EndingAddress
);
1768 /* Loop all the PTEs */
1769 while (PointerPte
<= LastPte
)
1771 /* Check if we've hit an new PDE boundary */
1774 /* Is this PDE demand zero? */
1775 PointerPde
= MiAddressToPte(PointerPte
);
1776 if (PointerPde
->u
.Long
!= 0)
1778 /* It isn't -- is it valid? */
1779 if (PointerPde
->u
.Hard
.Valid
== 0)
1781 /* Nope, fault it in */
1782 PointerPte
= MiPteToAddress(PointerPde
);
1783 MiMakeSystemAddressValid(PointerPte
, Process
);
1788 /* The PTE was already valid, so move to the next one */
1790 PointerPte
= MiPteToAddress(PointerPde
);
1792 /* Is the entire VAD committed? If not, fail */
1793 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1795 /* Everything is committed so far past the range, return true */
1796 if (PointerPte
> LastPte
) return TRUE
;
1800 /* Is the PTE demand zero? */
1801 if (PointerPte
->u
.Long
== 0)
1803 /* Is the entire VAD committed? If not, fail */
1804 if (!Vad
->u
.VadFlags
.MemCommit
) return FALSE
;
1808 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1809 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
1810 (PointerPte
->u
.Hard
.Valid
== 0) &&
1811 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
1812 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
1814 /* Then part of the range is decommitted, so fail */
1819 /* Move to the next PTE */
1821 OnBoundary
= MiIsPteOnPdeBoundary(PointerPte
);
1824 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1830 MiRosProtectVirtualMemory(IN PEPROCESS Process
,
1831 IN OUT PVOID
*BaseAddress
,
1832 IN OUT PSIZE_T NumberOfBytesToProtect
,
1833 IN ULONG NewAccessProtection
,
1834 OUT PULONG OldAccessProtection OPTIONAL
)
1836 PMEMORY_AREA MemoryArea
;
1837 PMMSUPPORT AddressSpace
;
1838 ULONG OldAccessProtection_
;
1841 *NumberOfBytesToProtect
= PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) - PAGE_ROUND_DOWN(*BaseAddress
);
1842 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
1844 AddressSpace
= &Process
->Vm
;
1845 MmLockAddressSpace(AddressSpace
);
1846 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
1847 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
1849 MmUnlockAddressSpace(AddressSpace
);
1850 return STATUS_UNSUCCESSFUL
;
1853 if (OldAccessProtection
== NULL
) OldAccessProtection
= &OldAccessProtection_
;
1855 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
);
1856 Status
= MmProtectSectionView(AddressSpace
,
1859 *NumberOfBytesToProtect
,
1860 NewAccessProtection
,
1861 OldAccessProtection
);
1863 MmUnlockAddressSpace(AddressSpace
);
1870 MiProtectVirtualMemory(IN PEPROCESS Process
,
1871 IN OUT PVOID
*BaseAddress
,
1872 IN OUT PSIZE_T NumberOfBytesToProtect
,
1873 IN ULONG NewAccessProtection
,
1874 OUT PULONG OldAccessProtection OPTIONAL
)
1876 PMEMORY_AREA MemoryArea
;
1878 PMMSUPPORT AddressSpace
;
1879 ULONG_PTR StartingAddress
, EndingAddress
;
1880 PMMPTE PointerPde
, PointerPte
, LastPte
;
1882 //PUSHORT UsedPageTableEntries;
1884 ULONG ProtectionMask
, OldProtect
;
1886 NTSTATUS Status
= STATUS_SUCCESS
;
1888 /* Calcualte base address for the VAD */
1889 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1890 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1892 /* Calculate the protection mask and make sure it's valid */
1893 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1894 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1896 DPRINT1("Invalid protection mask\n");
1897 return STATUS_INVALID_PAGE_PROTECTION
;
1900 /* Check for ROS specific memory area */
1901 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1902 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1905 return MiRosProtectVirtualMemory(Process
,
1907 NumberOfBytesToProtect
,
1908 NewAccessProtection
,
1909 OldAccessProtection
);
1912 /* Lock the address space and make sure the process isn't already dead */
1913 AddressSpace
= MmGetCurrentAddressSpace();
1914 MmLockAddressSpace(AddressSpace
);
1915 if (Process
->VmDeleted
)
1917 DPRINT1("Process is dying\n");
1918 Status
= STATUS_PROCESS_IS_TERMINATING
;
1922 /* Get the VAD for this address range, and make sure it exists */
1923 Vad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
1924 EndingAddress
>> PAGE_SHIFT
,
1928 DPRINT("Could not find a VAD for this allocation\n");
1929 Status
= STATUS_CONFLICTING_ADDRESSES
;
1933 /* Make sure the address is within this VAD's boundaries */
1934 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
1935 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
1937 Status
= STATUS_CONFLICTING_ADDRESSES
;
1941 /* These kinds of VADs are not supported atm */
1942 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
1943 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
1944 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
1946 DPRINT1("Illegal VAD for attempting to set protection\n");
1947 Status
= STATUS_CONFLICTING_ADDRESSES
;
1951 /* Check for a VAD whose protection can't be changed */
1952 if (Vad
->u
.VadFlags
.NoChange
== 1)
1954 DPRINT1("Trying to change protection of a NoChange VAD\n");
1955 Status
= STATUS_INVALID_PAGE_PROTECTION
;
1959 /* Is this section, or private memory? */
1960 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
1962 /* Not yet supported */
1963 if (Vad
->u
.VadFlags
.VadType
== VadLargePageSection
)
1965 DPRINT1("Illegal VAD for attempting to set protection\n");
1966 Status
= STATUS_CONFLICTING_ADDRESSES
;
1970 /* Rotate VADs are not yet supported */
1971 if (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
)
1973 DPRINT1("Illegal VAD for attempting to set protection\n");
1974 Status
= STATUS_CONFLICTING_ADDRESSES
;
1978 /* Not valid on section files */
1979 if (NewAccessProtection
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
))
1982 DPRINT1("Invalid protection flags for section\n");
1983 Status
= STATUS_INVALID_PARAMETER_4
;
1987 /* Check if data or page file mapping protection PTE is compatible */
1988 if (!Vad
->ControlArea
->u
.Flags
.Image
)
1991 DPRINT1("Fixme: Not checking for valid protection\n");
1994 /* This is a section, and this is not yet supported */
1995 DPRINT1("Section protection not yet supported\n");
2000 /* Private memory, check protection flags */
2001 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
2002 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
2004 DPRINT1("Invalid protection flags for private memory\n");
2005 Status
= STATUS_INVALID_PARAMETER_4
;
2009 //MiLockProcessWorkingSet(Thread, Process);
2011 /* Check if all pages in this range are committed */
2012 Committed
= MiIsEntireRangeCommitted(StartingAddress
,
2019 DPRINT1("The entire range is not committed\n");
2020 Status
= STATUS_NOT_COMMITTED
;
2021 //MiUnlockProcessWorkingSet(Thread, Process);
2025 /* Compute starting and ending PTE and PDE addresses */
2026 PointerPde
= MiAddressToPde(StartingAddress
);
2027 PointerPte
= MiAddressToPte(StartingAddress
);
2028 LastPte
= MiAddressToPte(EndingAddress
);
2030 /* Make this PDE valid */
2031 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2033 /* Save protection of the first page */
2034 if (PointerPte
->u
.Long
!= 0)
2036 /* Capture the page protection and make the PDE valid */
2037 OldProtect
= MiGetPageProtection(PointerPte
);
2038 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2042 /* Grab the old protection from the VAD itself */
2043 OldProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
2046 /* Loop all the PTEs now */
2047 while (PointerPte
<= LastPte
)
2049 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2050 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
2052 PointerPde
= MiAddressToPte(PointerPte
);
2053 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2056 /* Capture the PTE and see what we're dealing with */
2057 PteContents
= *PointerPte
;
2058 if (PteContents
.u
.Long
== 0)
2060 /* This used to be a zero PTE and it no longer is, so we must add a
2061 reference to the pagetable. */
2062 //UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(MiPteToAddress(PointerPte))];
2063 //(*UsedPageTableEntries)++;
2064 //ASSERT((*UsedPageTableEntries) <= PTE_COUNT);
2065 DPRINT1("HACK: Not increasing UsedPageTableEntries count!\n");
2067 else if (PteContents
.u
.Hard
.Valid
== 1)
2069 /* Get the PFN entry */
2070 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2072 /* We don't support this yet */
2073 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2075 /* Check if the page should not be accessible at all */
2076 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
2077 (NewAccessProtection
& PAGE_GUARD
))
2079 /* The page should be in the WS and we should make it transition now */
2084 /* Write the protection mask and write it with a TLB flush */
2085 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2086 MiFlushTbAndCapture(Vad
,
2094 /* We don't support these cases yet */
2095 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2096 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2098 /* The PTE is already demand-zero, just update the protection mask */
2099 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
2100 ASSERT(PointerPte
->u
.Long
!= 0);
2103 /* Move to the next PTE */
2107 /* Unlock the working set */
2108 //MiUnlockProcessWorkingSet(Thread, Process);
2111 /* Unlock the address space */
2112 MmUnlockAddressSpace(AddressSpace
);
2114 /* Return parameters and success */
2115 *NumberOfBytesToProtect
= EndingAddress
- StartingAddress
+ 1;
2116 *BaseAddress
= (PVOID
)StartingAddress
;
2117 *OldAccessProtection
= OldProtect
;
2118 return STATUS_SUCCESS
;
2121 /* Unlock the address space and return the failure code */
2122 MmUnlockAddressSpace(AddressSpace
);
2128 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
2129 IN PEPROCESS TargetProcess
,
2132 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
2135 // Sanity checks. The latter is because we only use this function with the
2136 // PFN lock not held, so it may go away in the future.
2138 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2139 ASSERT(OldIrql
== MM_NOIRQL
);
2142 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2143 // return the same address as the PDE on 2-level page table systems.
2145 // If everything is already valid, there is nothing to do.
2147 PointerPpe
= MiAddressToPte(PointerPde
);
2148 PointerPxe
= MiAddressToPde(PointerPde
);
2149 if ((PointerPxe
->u
.Hard
.Valid
) &&
2150 (PointerPpe
->u
.Hard
.Valid
) &&
2151 (PointerPde
->u
.Hard
.Valid
))
2157 // At least something is invalid, so begin by getting the PTE for the PDE itself
2158 // and then lookup each additional level. We must do it in this precise order
2159 // because the pagfault.c code (as well as in Windows) depends that the next
2160 // level up (higher) must be valid when faulting a lower level
2162 PointerPte
= MiPteToAddress(PointerPde
);
2166 // Make sure APCs continued to be disabled
2168 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2171 // First, make the PXE valid if needed
2173 if (!PointerPxe
->u
.Hard
.Valid
)
2175 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2176 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2182 if (!PointerPpe
->u
.Hard
.Valid
)
2184 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2185 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2189 // And finally, make the PDE itself valid.
2191 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2194 // This should've worked the first time so the loop is really just for
2195 // show -- ASSERT that we're actually NOT going to be looping.
2197 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2198 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2199 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2200 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2201 !(PointerPpe
->u
.Hard
.Valid
) ||
2202 !(PointerPde
->u
.Hard
.Valid
));
2207 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2213 PFN_NUMBER PageFrameIndex
;
2217 // Acquire the PFN lock and loop all the PTEs in the list
2219 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2220 for (i
= 0; i
!= Count
; i
++)
2223 // The PTE must currently be valid
2225 TempPte
= *ValidPteList
[i
];
2226 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2229 // Get the PFN entry for the page itself, and then for its page table
2231 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2232 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2233 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2236 // Decrement the share count on the page table, and then on the page
2239 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2240 MI_SET_PFN_DELETED(Pfn1
);
2241 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2244 // Make the page decommitted
2246 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2250 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2251 // and then release the PFN lock
2254 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2259 MiDecommitPages(IN PVOID StartingAddress
,
2260 IN PMMPTE EndingPte
,
2261 IN PEPROCESS Process
,
2264 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2265 ULONG CommitReduction
= 0;
2266 PMMPTE ValidPteList
[256];
2270 PUSHORT UsedPageTableEntries
;
2271 PETHREAD CurrentThread
= PsGetCurrentThread();
2274 // Get the PTE and PTE for the address, and lock the working set
2275 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2276 // commited range ends so that we can do the right accounting.
2278 PointerPde
= MiAddressToPde(StartingAddress
);
2279 PointerPte
= MiAddressToPte(StartingAddress
);
2280 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2281 MiLockWorkingSet(CurrentThread
, &Process
->Vm
);
2284 // Make the PDE valid, and now loop through each page's worth of data
2286 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2287 while (PointerPte
<= EndingPte
)
2290 // Check if we've crossed a PDE boundary
2292 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
2295 // Get the new PDE and flush the valid PTEs we had built up until
2296 // now. This helps reduce the amount of TLB flushing we have to do.
2297 // Note that Windows does a much better job using timestamps and
2298 // such, and does not flush the entire TLB all the time, but right
2299 // now we have bigger problems to worry about than TLB flushing.
2301 PointerPde
= MiAddressToPde(StartingAddress
);
2304 MiProcessValidPteList(ValidPteList
, PteCount
);
2309 // Make this PDE valid
2311 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2315 // Read this PTE. It might be active or still demand-zero.
2317 PteContents
= *PointerPte
;
2318 if (PteContents
.u
.Long
)
2321 // The PTE is active. It might be valid and in a working set, or
2322 // it might be a prototype PTE or paged out or even in transition.
2324 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2327 // It's already decommited, so there's nothing for us to do here
2334 // Remove it from the counters, and check if it was valid or not
2336 //Process->NumberOfPrivatePages--;
2337 if (PteContents
.u
.Hard
.Valid
)
2340 // It's valid. At this point make sure that it is not a ROS
2341 // PFN. Also, we don't support ProtoPTEs in this code path.
2343 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2344 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2345 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2348 // Flush any pending PTEs that we had not yet flushed, if our
2349 // list has gotten too big, then add this PTE to the flush list.
2351 if (PteCount
== 256)
2353 MiProcessValidPteList(ValidPteList
, PteCount
);
2356 ValidPteList
[PteCount
++] = PointerPte
;
2361 // We do not support any of these other scenarios at the moment
2363 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2364 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2365 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2368 // So the only other possibility is that it is still a demand
2369 // zero PTE, in which case we undo the accounting we did
2370 // earlier and simply make the page decommitted.
2372 //Process->NumberOfPrivatePages++;
2373 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2380 // This used to be a zero PTE and it no longer is, so we must add a
2381 // reference to the pagetable.
2383 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(StartingAddress
)];
2384 (*UsedPageTableEntries
)++;
2385 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
2388 // Next, we account for decommitted PTEs and make the PTE as such
2390 if (PointerPte
> CommitPte
) CommitReduction
++;
2391 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2395 // Move to the next PTE and the next address
2398 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2402 // Flush any dangling PTEs from the loop in the last page table, and then
2403 // release the working set and return the commit reduction accounting.
2405 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2406 MiUnlockWorkingSet(CurrentThread
, &Process
->Vm
);
2407 return CommitReduction
;
2410 /* PUBLIC FUNCTIONS ***********************************************************/
2417 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2428 MmSecureVirtualMemory(IN PVOID Address
,
2432 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2441 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2443 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2446 /* SYSTEM CALLS ***************************************************************/
2450 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2451 IN PVOID BaseAddress
,
2453 IN SIZE_T NumberOfBytesToRead
,
2454 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2456 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2458 NTSTATUS Status
= STATUS_SUCCESS
;
2459 SIZE_T BytesRead
= 0;
2463 // Check if we came from user mode
2465 if (PreviousMode
!= KernelMode
)
2468 // Validate the read addresses
2470 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2471 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2472 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2473 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2476 // Don't allow to write into kernel space
2478 return STATUS_ACCESS_VIOLATION
;
2482 // Enter SEH for probe
2487 // Probe the output value
2489 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2491 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2494 // Get exception code
2496 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2502 // Don't do zero-byte transfers
2504 if (NumberOfBytesToRead
)
2507 // Reference the process
2509 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2515 if (NT_SUCCESS(Status
))
2520 Status
= MmCopyVirtualMemory(Process
,
2522 PsGetCurrentProcess(),
2524 NumberOfBytesToRead
,
2529 // Dereference the process
2531 ObDereferenceObject(Process
);
2536 // Check if the caller sent this parameter
2538 if (NumberOfBytesRead
)
2541 // Enter SEH to guard write
2546 // Return the number of bytes read
2548 *NumberOfBytesRead
= BytesRead
;
2550 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2564 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2565 IN PVOID BaseAddress
,
2567 IN SIZE_T NumberOfBytesToWrite
,
2568 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2570 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2572 NTSTATUS Status
= STATUS_SUCCESS
;
2573 SIZE_T BytesWritten
= 0;
2577 // Check if we came from user mode
2579 if (PreviousMode
!= KernelMode
)
2582 // Validate the read addresses
2584 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2585 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2586 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2587 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2590 // Don't allow to write into kernel space
2592 return STATUS_ACCESS_VIOLATION
;
2596 // Enter SEH for probe
2601 // Probe the output value
2603 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2605 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2608 // Get exception code
2610 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2616 // Don't do zero-byte transfers
2618 if (NumberOfBytesToWrite
)
2621 // Reference the process
2623 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2629 if (NT_SUCCESS(Status
))
2634 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2638 NumberOfBytesToWrite
,
2643 // Dereference the process
2645 ObDereferenceObject(Process
);
2650 // Check if the caller sent this parameter
2652 if (NumberOfBytesWritten
)
2655 // Enter SEH to guard write
2660 // Return the number of bytes written
2662 *NumberOfBytesWritten
= BytesWritten
;
2664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2678 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2679 IN OUT PVOID
*UnsafeBaseAddress
,
2680 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2681 IN ULONG NewAccessProtection
,
2682 OUT PULONG UnsafeOldAccessProtection
)
2685 ULONG OldAccessProtection
;
2687 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2688 PVOID BaseAddress
= NULL
;
2689 SIZE_T NumberOfBytesToProtect
= 0;
2690 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2692 BOOLEAN Attached
= FALSE
;
2693 KAPC_STATE ApcState
;
2697 // Check for valid protection flags
2699 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2700 if (Protection
!= PAGE_NOACCESS
&&
2701 Protection
!= PAGE_READONLY
&&
2702 Protection
!= PAGE_READWRITE
&&
2703 Protection
!= PAGE_WRITECOPY
&&
2704 Protection
!= PAGE_EXECUTE
&&
2705 Protection
!= PAGE_EXECUTE_READ
&&
2706 Protection
!= PAGE_EXECUTE_READWRITE
&&
2707 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2712 return STATUS_INVALID_PAGE_PROTECTION
;
2716 // Check if we came from user mode
2718 if (PreviousMode
!= KernelMode
)
2721 // Enter SEH for probing
2726 // Validate all outputs
2728 ProbeForWritePointer(UnsafeBaseAddress
);
2729 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2730 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2735 BaseAddress
= *UnsafeBaseAddress
;
2736 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2738 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2741 // Get exception code
2743 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2752 BaseAddress
= *UnsafeBaseAddress
;
2753 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2757 // Catch illegal base address
2759 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2762 // Catch illegal region size
2764 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2769 return STATUS_INVALID_PARAMETER_3
;
2773 // 0 is also illegal
2775 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2778 // Get a reference to the process
2780 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2781 PROCESS_VM_OPERATION
,
2786 if (!NT_SUCCESS(Status
)) return Status
;
2789 // Check if we should attach
2791 if (CurrentProcess
!= Process
)
2796 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2801 // Do the actual work
2803 Status
= MiProtectVirtualMemory(Process
,
2805 &NumberOfBytesToProtect
,
2806 NewAccessProtection
,
2807 &OldAccessProtection
);
2812 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2815 // Release reference
2817 ObDereferenceObject(Process
);
2820 // Enter SEH to return data
2825 // Return data to user
2827 *UnsafeOldAccessProtection
= OldAccessProtection
;
2828 *UnsafeBaseAddress
= BaseAddress
;
2829 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2831 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2844 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2845 IN OUT PVOID
*BaseAddress
,
2846 IN OUT PSIZE_T NumberOfBytesToLock
,
2850 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2852 BOOLEAN Attached
= FALSE
;
2853 KAPC_STATE ApcState
;
2854 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2855 PVOID CapturedBaseAddress
;
2856 SIZE_T CapturedBytesToLock
;
2862 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2865 // Invalid set of flags
2867 return STATUS_INVALID_PARAMETER
;
2871 // At least one flag must be specified
2873 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2878 return STATUS_INVALID_PARAMETER
;
2882 // Enter SEH for probing
2887 // Validate output data
2889 ProbeForWritePointer(BaseAddress
);
2890 ProbeForWriteSize_t(NumberOfBytesToLock
);
2895 CapturedBaseAddress
= *BaseAddress
;
2896 CapturedBytesToLock
= *NumberOfBytesToLock
;
2898 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2901 // Get exception code
2903 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2908 // Catch illegal base address
2910 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2913 // Catch illegal region size
2915 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2920 return STATUS_INVALID_PARAMETER
;
2924 // 0 is also illegal
2926 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2929 // Get a reference to the process
2931 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2932 PROCESS_VM_OPERATION
,
2937 if (!NT_SUCCESS(Status
)) return Status
;
2940 // Check if this is is system-mapped
2942 if (MapType
& MAP_SYSTEM
)
2945 // Check for required privilege
2947 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2950 // Fail: Don't have it
2952 ObDereferenceObject(Process
);
2953 return STATUS_PRIVILEGE_NOT_HELD
;
2958 // Check if we should attach
2960 if (CurrentProcess
!= Process
)
2965 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2977 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2980 // Release reference
2982 ObDereferenceObject(Process
);
2985 // Enter SEH to return data
2990 // Return data to user
2992 *BaseAddress
= CapturedBaseAddress
;
2993 *NumberOfBytesToLock
= 0;
2995 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2998 // Get exception code
3000 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3007 return STATUS_SUCCESS
;
3012 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
3013 IN OUT PVOID
*BaseAddress
,
3014 IN OUT PSIZE_T NumberOfBytesToUnlock
,
3018 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3020 BOOLEAN Attached
= FALSE
;
3021 KAPC_STATE ApcState
;
3022 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3023 PVOID CapturedBaseAddress
;
3024 SIZE_T CapturedBytesToUnlock
;
3030 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
3033 // Invalid set of flags
3035 return STATUS_INVALID_PARAMETER
;
3039 // At least one flag must be specified
3041 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
3046 return STATUS_INVALID_PARAMETER
;
3050 // Enter SEH for probing
3055 // Validate output data
3057 ProbeForWritePointer(BaseAddress
);
3058 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
3063 CapturedBaseAddress
= *BaseAddress
;
3064 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
3066 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3069 // Get exception code
3071 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3076 // Catch illegal base address
3078 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3081 // Catch illegal region size
3083 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
3088 return STATUS_INVALID_PARAMETER
;
3092 // 0 is also illegal
3094 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
3097 // Get a reference to the process
3099 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3100 PROCESS_VM_OPERATION
,
3105 if (!NT_SUCCESS(Status
)) return Status
;
3108 // Check if this is is system-mapped
3110 if (MapType
& MAP_SYSTEM
)
3113 // Check for required privilege
3115 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
3118 // Fail: Don't have it
3120 ObDereferenceObject(Process
);
3121 return STATUS_PRIVILEGE_NOT_HELD
;
3126 // Check if we should attach
3128 if (CurrentProcess
!= Process
)
3133 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3145 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3148 // Release reference
3150 ObDereferenceObject(Process
);
3153 // Enter SEH to return data
3158 // Return data to user
3160 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3161 *NumberOfBytesToUnlock
= 0;
3163 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3166 // Get exception code
3168 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3175 return STATUS_SUCCESS
;
3180 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3181 IN OUT PVOID
*BaseAddress
,
3182 IN OUT PSIZE_T NumberOfBytesToFlush
,
3183 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3187 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3188 PVOID CapturedBaseAddress
;
3189 SIZE_T CapturedBytesToFlush
;
3190 IO_STATUS_BLOCK LocalStatusBlock
;
3194 // Check if we came from user mode
3196 if (PreviousMode
!= KernelMode
)
3199 // Enter SEH for probing
3204 // Validate all outputs
3206 ProbeForWritePointer(BaseAddress
);
3207 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3208 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3213 CapturedBaseAddress
= *BaseAddress
;
3214 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3216 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3219 // Get exception code
3221 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3230 CapturedBaseAddress
= *BaseAddress
;
3231 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3235 // Catch illegal base address
3237 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3240 // Catch illegal region size
3242 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3247 return STATUS_INVALID_PARAMETER
;
3251 // Get a reference to the process
3253 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3254 PROCESS_VM_OPERATION
,
3259 if (!NT_SUCCESS(Status
)) return Status
;
3264 Status
= MmFlushVirtualMemory(Process
,
3265 &CapturedBaseAddress
,
3266 &CapturedBytesToFlush
,
3270 // Release reference
3272 ObDereferenceObject(Process
);
3275 // Enter SEH to return data
3280 // Return data to user
3282 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3283 *NumberOfBytesToFlush
= 0;
3284 *IoStatusBlock
= LocalStatusBlock
;
3286 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3302 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3304 IN PVOID BaseAddress
,
3305 IN SIZE_T RegionSize
,
3306 IN PVOID
*UserAddressArray
,
3307 OUT PULONG_PTR EntriesInUserAddressArray
,
3308 OUT PULONG Granularity
)
3313 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3314 ULONG_PTR CapturedEntryCount
;
3318 // Check if we came from user mode
3320 if (PreviousMode
!= KernelMode
)
3323 // Enter SEH for probing
3328 // Catch illegal base address
3330 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3333 // Catch illegal region size
3335 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3340 return STATUS_INVALID_PARAMETER_3
;
3344 // Validate all data
3346 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3347 ProbeForWriteUlong(Granularity
);
3352 CapturedEntryCount
= *EntriesInUserAddressArray
;
3355 // Must have a count
3357 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
3360 // Can't be larger than the maximum
3362 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3367 return STATUS_INVALID_PARAMETER_5
;
3371 // Probe the actual array
3373 ProbeForWrite(UserAddressArray
,
3374 CapturedEntryCount
* sizeof(PVOID
),
3377 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3380 // Get exception code
3382 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3391 CapturedEntryCount
= *EntriesInUserAddressArray
;
3392 ASSERT(CapturedEntryCount
!= 0);
3396 // Check if this is a local request
3398 if (ProcessHandle
== NtCurrentProcess())
3401 // No need to reference the process
3403 Process
= PsGetCurrentProcess();
3408 // Reference the target
3410 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3411 PROCESS_VM_OPERATION
,
3416 if (!NT_SUCCESS(Status
)) return Status
;
3420 // Compute the last address and validate it
3422 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3423 if (BaseAddress
> EndAddress
)
3428 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3429 return STATUS_INVALID_PARAMETER_4
;
3438 // Dereference if needed
3440 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3443 // Enter SEH to return data
3448 // Return data to user
3450 *EntriesInUserAddressArray
= 0;
3451 *Granularity
= PAGE_SIZE
;
3453 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3456 // Get exception code
3458 Status
= _SEH2_GetExceptionCode();
3465 return STATUS_SUCCESS
;
3473 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3474 IN PVOID BaseAddress
,
3475 IN SIZE_T RegionSize
)
3480 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3481 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3484 // Catch illegal base address
3486 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3489 // Catch illegal region size
3491 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3496 return STATUS_INVALID_PARAMETER_3
;
3500 // Check if this is a local request
3502 if (ProcessHandle
== NtCurrentProcess())
3505 // No need to reference the process
3507 Process
= PsGetCurrentProcess();
3512 // Reference the target
3514 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3515 PROCESS_VM_OPERATION
,
3520 if (!NT_SUCCESS(Status
)) return Status
;
3524 // Compute the last address and validate it
3526 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3527 if (BaseAddress
> EndAddress
)
3532 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3533 return STATUS_INVALID_PARAMETER_3
;
3542 // Dereference if needed
3544 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3549 return STATUS_SUCCESS
;
3554 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3555 IN PVOID BaseAddress
,
3556 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3557 OUT PVOID MemoryInformation
,
3558 IN SIZE_T MemoryInformationLength
,
3559 OUT PSIZE_T ReturnLength
)
3561 NTSTATUS Status
= STATUS_SUCCESS
;
3562 KPROCESSOR_MODE PreviousMode
;
3564 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3566 /* Bail out if the address is invalid */
3567 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3569 /* Probe return buffer */
3570 PreviousMode
= ExGetPreviousMode();
3571 if (PreviousMode
!= KernelMode
)
3575 ProbeForWrite(MemoryInformation
,
3576 MemoryInformationLength
,
3579 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3583 Status
= _SEH2_GetExceptionCode();
3587 if (!NT_SUCCESS(Status
))
3593 switch(MemoryInformationClass
)
3595 case MemoryBasicInformation
:
3596 /* Validate the size information of the class */
3597 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3599 /* The size is invalid */
3600 return STATUS_INFO_LENGTH_MISMATCH
;
3602 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3605 MemoryInformationLength
,
3609 case MemorySectionName
:
3610 /* Validate the size information of the class */
3611 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3613 /* The size is invalid */
3614 return STATUS_INFO_LENGTH_MISMATCH
;
3616 Status
= MiQueryMemorySectionName(ProcessHandle
,
3619 MemoryInformationLength
,
3622 case MemoryWorkingSetList
:
3623 case MemoryBasicVlmInformation
:
3625 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3637 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3638 IN OUT PVOID
* UBaseAddress
,
3639 IN ULONG_PTR ZeroBits
,
3640 IN OUT PSIZE_T URegionSize
,
3641 IN ULONG AllocationType
,
3645 PMEMORY_AREA MemoryArea
;
3646 PFN_NUMBER PageCount
;
3647 PMMVAD Vad
, FoundVad
;
3648 PUSHORT UsedPageTableEntries
;
3650 PMMSUPPORT AddressSpace
;
3652 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3653 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3654 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3655 PETHREAD CurrentThread
= PsGetCurrentThread();
3656 KAPC_STATE ApcState
;
3657 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3658 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3660 PMMPTE PointerPte
, PointerPde
, LastPte
;
3663 /* Check for valid Zero bits */
3666 DPRINT1("Too many zero bits\n");
3667 return STATUS_INVALID_PARAMETER_3
;
3670 /* Check for valid Allocation Types */
3671 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3672 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3674 DPRINT1("Invalid Allocation Type\n");
3675 return STATUS_INVALID_PARAMETER_5
;
3678 /* Check for at least one of these Allocation Types to be set */
3679 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3681 DPRINT1("No memory allocation base type\n");
3682 return STATUS_INVALID_PARAMETER_5
;
3685 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3686 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3688 DPRINT1("Invalid use of MEM_RESET\n");
3689 return STATUS_INVALID_PARAMETER_5
;
3692 /* Check if large pages are being used */
3693 if (AllocationType
& MEM_LARGE_PAGES
)
3695 /* Large page allocations MUST be committed */
3696 if (!(AllocationType
& MEM_COMMIT
))
3698 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3699 return STATUS_INVALID_PARAMETER_5
;
3702 /* These flags are not allowed with large page allocations */
3703 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3705 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3706 return STATUS_INVALID_PARAMETER_5
;
3710 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3711 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3713 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3714 return STATUS_INVALID_PARAMETER_5
;
3717 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3718 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3720 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3721 return STATUS_INVALID_PARAMETER_5
;
3724 /* Check for valid MEM_PHYSICAL usage */
3725 if (AllocationType
& MEM_PHYSICAL
)
3727 /* Only these flags are allowed with MEM_PHYSIAL */
3728 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3730 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3731 return STATUS_INVALID_PARAMETER_5
;
3734 /* Then make sure PAGE_READWRITE is used */
3735 if (Protect
!= PAGE_READWRITE
)
3737 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3738 return STATUS_INVALID_PARAMETER_6
;
3743 // Force PAGE_READWRITE for everything, for now
3745 Protect
= PAGE_READWRITE
;
3747 /* Calculate the protection mask and make sure it's valid */
3748 ProtectionMask
= MiMakeProtectionMask(Protect
);
3749 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3751 DPRINT1("Invalid protection mask\n");
3752 return STATUS_INVALID_PAGE_PROTECTION
;
3758 /* Check for user-mode parameters */
3759 if (PreviousMode
!= KernelMode
)
3761 /* Make sure they are writable */
3762 ProbeForWritePointer(UBaseAddress
);
3763 ProbeForWriteUlong(URegionSize
);
3766 /* Capture their values */
3767 PBaseAddress
= *UBaseAddress
;
3768 PRegionSize
= *URegionSize
;
3770 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3772 /* Return the exception code */
3773 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3777 /* Make sure the allocation isn't past the VAD area */
3778 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3780 DPRINT1("Virtual allocation base above User Space\n");
3781 return STATUS_INVALID_PARAMETER_2
;
3784 /* Make sure the allocation wouldn't overflow past the VAD area */
3785 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3787 DPRINT1("Region size would overflow into kernel-memory\n");
3788 return STATUS_INVALID_PARAMETER_4
;
3791 /* Make sure there's a size specified */
3794 DPRINT1("Region size is invalid (zero)\n");
3795 return STATUS_INVALID_PARAMETER_4
;
3799 // If this is for the current process, just use PsGetCurrentProcess
3801 if (ProcessHandle
== NtCurrentProcess())
3803 Process
= CurrentProcess
;
3808 // Otherwise, reference the process with VM rights and attach to it if
3809 // this isn't the current process. We must attach because we'll be touching
3810 // PTEs and PDEs that belong to user-mode memory, and also touching the
3811 // Working Set which is stored in Hyperspace.
3813 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3814 PROCESS_VM_OPERATION
,
3819 if (!NT_SUCCESS(Status
)) return Status
;
3820 if (CurrentProcess
!= Process
)
3822 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3828 // Check for large page allocations and make sure that the required privilege
3829 // is being held, before attempting to handle them.
3831 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3832 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3834 /* Fail without it */
3835 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3836 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3837 goto FailPathNoLock
;
3841 // Fail on the things we don't yet support
3845 DPRINT1("Zero bits not supported\n");
3846 Status
= STATUS_INVALID_PARAMETER
;
3847 goto FailPathNoLock
;
3849 if ((AllocationType
& MEM_LARGE_PAGES
) == MEM_LARGE_PAGES
)
3851 DPRINT1("MEM_LARGE_PAGES not supported\n");
3852 Status
= STATUS_INVALID_PARAMETER
;
3853 goto FailPathNoLock
;
3855 if ((AllocationType
& MEM_PHYSICAL
) == MEM_PHYSICAL
)
3857 DPRINT1("MEM_PHYSICAL not supported\n");
3858 Status
= STATUS_INVALID_PARAMETER
;
3859 goto FailPathNoLock
;
3861 if ((AllocationType
& MEM_WRITE_WATCH
) == MEM_WRITE_WATCH
)
3863 DPRINT1("MEM_WRITE_WATCH not supported\n");
3864 Status
= STATUS_INVALID_PARAMETER
;
3865 goto FailPathNoLock
;
3867 if ((AllocationType
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
)
3869 DPRINT1("MEM_TOP_DOWN not supported\n");
3870 Status
= STATUS_INVALID_PARAMETER
;
3871 goto FailPathNoLock
;
3873 if ((AllocationType
& MEM_RESET
) == MEM_RESET
)
3875 DPRINT1("MEM_RESET not supported\n");
3876 Status
= STATUS_INVALID_PARAMETER
;
3877 goto FailPathNoLock
;
3879 if (Process
->VmTopDown
== 1)
3881 DPRINT1("VmTopDown not supported\n");
3882 Status
= STATUS_INVALID_PARAMETER
;
3883 goto FailPathNoLock
;
3887 // Check if the caller is reserving memory, or committing memory and letting
3888 // us pick the base address
3890 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3893 // Do not allow COPY_ON_WRITE through this API
3895 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3897 DPRINT1("Copy on write not allowed through this path\n");
3898 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3899 goto FailPathNoLock
;
3903 // Does the caller have an address in mind, or is this a blind commit?
3908 // This is a blind commit, all we need is the region size
3910 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3911 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3913 StartingAddress
= 0;
3918 // This is a reservation, so compute the starting address on the
3919 // expected 64KB granularity, and see where the ending address will
3920 // fall based on the aligned address and the passed in region size
3922 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3923 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3924 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3928 // Allocate and initialize the VAD
3930 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3931 ASSERT(Vad
!= NULL
);
3932 Vad
->u
.LongFlags
= 0;
3933 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3934 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3935 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3936 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3939 // Lock the address space and make sure the process isn't already dead
3941 AddressSpace
= MmGetCurrentAddressSpace();
3942 MmLockAddressSpace(AddressSpace
);
3943 if (Process
->VmDeleted
)
3945 Status
= STATUS_PROCESS_IS_TERMINATING
;
3950 // Did we have a base address? If no, find a valid address that is 64KB
3951 // aligned in the VAD tree. Otherwise, make sure that the address range
3952 // which was passed in isn't already conflicting with an existing address
3957 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3960 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3962 if (!NT_SUCCESS(Status
)) goto FailPath
;
3965 // Now we know where the allocation ends. Make sure it doesn't end up
3966 // somewhere in kernel mode.
3968 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3969 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3971 Status
= STATUS_NO_MEMORY
;
3975 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3976 EndingAddress
>> PAGE_SHIFT
,
3980 // The address specified is in conflict!
3982 Status
= STATUS_CONFLICTING_ADDRESSES
;
3987 // Write out the VAD fields for this allocation
3989 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3990 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3993 // FIXME: Should setup VAD bitmap
3995 Status
= STATUS_SUCCESS
;
3998 // Lock the working set and insert the VAD into the process VAD tree
4000 MiLockProcessWorkingSet(Process
, CurrentThread
);
4001 Vad
->ControlArea
= NULL
; // For Memory-Area hack
4002 MiInsertVad(Vad
, Process
);
4003 MiUnlockProcessWorkingSet(Process
, CurrentThread
);
4006 // Update the virtual size of the process, and if this is now the highest
4007 // virtual size we have ever seen, update the peak virtual size to reflect
4010 Process
->VirtualSize
+= PRegionSize
;
4011 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
4013 Process
->PeakVirtualSize
= Process
->VirtualSize
;
4017 // Release address space and detach and dereference the target process if
4018 // it was different from the current process
4020 MmUnlockAddressSpace(AddressSpace
);
4021 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4022 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4025 // Use SEH to write back the base address and the region size. In the case
4026 // of an exception, we do not return back the exception code, as the memory
4027 // *has* been allocated. The caller would now have to call VirtualQuery
4028 // or do some other similar trick to actually find out where its memory
4029 // allocation ended up
4033 *URegionSize
= PRegionSize
;
4034 *UBaseAddress
= (PVOID
)StartingAddress
;
4036 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4040 return STATUS_SUCCESS
;
4044 // This is a MEM_COMMIT on top of an existing address which must have been
4045 // MEM_RESERVED already. Compute the start and ending base addresses based
4046 // on the user input, and then compute the actual region size once all the
4047 // alignments have been done.
4049 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4050 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
4051 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4054 // Lock the address space and make sure the process isn't already dead
4056 AddressSpace
= MmGetCurrentAddressSpace();
4057 MmLockAddressSpace(AddressSpace
);
4058 if (Process
->VmDeleted
)
4060 DPRINT1("Process is dying\n");
4061 Status
= STATUS_PROCESS_IS_TERMINATING
;
4066 // Get the VAD for this address range, and make sure it exists
4068 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
4069 EndingAddress
>> PAGE_SHIFT
,
4073 DPRINT1("Could not find a VAD for this allocation\n");
4074 Status
= STATUS_CONFLICTING_ADDRESSES
;
4079 // These kinds of VADs are illegal for this Windows function when trying to
4080 // commit an existing range
4082 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
4083 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
4084 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
4086 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4087 Status
= STATUS_CONFLICTING_ADDRESSES
;
4092 // Make sure that this address range actually fits within the VAD for it
4094 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
4095 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
4097 DPRINT1("Address range does not fit into the VAD\n");
4098 Status
= STATUS_CONFLICTING_ADDRESSES
;
4103 // Make sure this is an ARM3 section
4105 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
4106 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4108 // Is this a previously reserved section being committed? If so, enter the
4109 // special section path
4111 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
4114 // You cannot commit large page sections through this API
4116 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
4118 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4119 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4124 // You can only use caching flags on a rotate VAD
4126 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
4127 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
4129 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4130 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4135 // We should make sure that the section's permissions aren't being messed with
4137 if (FoundVad
->u
.VadFlags
.NoChange
)
4139 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
4143 // ARM3 does not support file-backed sections, only shared memory
4145 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
4148 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4150 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
4151 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
4153 DPRINT1("Invalid page protection for rotate VAD\n");
4154 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4159 // Compute PTE addresses and the quota charge, then grab the commit lock
4161 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
4162 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
4163 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
4164 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
4167 // Get the segment template PTE and start looping each page
4169 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
4170 ASSERT(TempPte
.u
.Long
!= 0);
4171 while (PointerPte
<= LastPte
)
4174 // For each non-already-committed page, write the invalid template PTE
4176 if (PointerPte
->u
.Long
== 0)
4178 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4188 // Now do the commit accounting and release the lock
4190 ASSERT(QuotaCharge
>= QuotaFree
);
4191 QuotaCharge
-= QuotaFree
;
4192 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4193 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4196 // We are done with committing the section pages
4198 Status
= STATUS_SUCCESS
;
4203 // This is a specific ReactOS check because we only use normal VADs
4205 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4208 // While this is an actual Windows check
4210 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4213 // Throw out attempts to use copy-on-write through this API path
4215 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4217 DPRINT1("Write copy attempted when not allowed\n");
4218 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4223 // Initialize a demand-zero PTE
4226 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4229 // Get the PTE, PDE and the last PTE for this address range
4231 PointerPde
= MiAddressToPde(StartingAddress
);
4232 PointerPte
= MiAddressToPte(StartingAddress
);
4233 LastPte
= MiAddressToPte(EndingAddress
);
4236 // Update the commit charge in the VAD as well as in the process, and check
4237 // if this commit charge was now higher than the last recorded peak, in which
4238 // case we also update the peak
4240 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4241 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4242 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4244 Process
->CommitChargePeak
= Process
->CommitCharge
;
4248 // Lock the working set while we play with user pages and page tables
4250 //MiLockWorkingSet(CurrentThread, AddressSpace);
4253 // Make the current page table valid, and then loop each page within it
4255 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4256 while (PointerPte
<= LastPte
)
4259 // Have we crossed into a new page table?
4261 if (!(((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)))
4264 // Get the PDE and now make it valid too
4266 PointerPde
= MiAddressToPte(PointerPte
);
4267 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4271 // Is this a zero PTE as expected?
4273 if (PointerPte
->u
.Long
== 0)
4276 // First increment the count of pages in the page table for this
4279 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
4280 (*UsedPageTableEntries
)++;
4281 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
4284 // And now write the invalid demand-zero PTE as requested
4286 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4288 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4291 // If the PTE was already decommitted, there is nothing else to do
4292 // but to write the new demand-zero PTE
4294 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4296 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4299 // We don't handle these scenarios yet
4301 if (PointerPte
->u
.Soft
.Valid
== 0)
4303 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4304 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4308 // There's a change in protection, remember this for later, but do
4309 // not yet handle it.
4311 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect
);
4312 ChangeProtection
= TRUE
;
4316 // Move to the next PTE
4322 // This path is not yet handled
4324 ASSERT(ChangeProtection
== FALSE
);
4327 // Release the working set lock, unlock the address space, and detach from
4328 // the target process if it was not the current process. Also dereference the
4329 // target process if this wasn't the case.
4331 //MiUnlockProcessWorkingSet(Process, CurrentThread);
4332 Status
= STATUS_SUCCESS
;
4334 MmUnlockAddressSpace(AddressSpace
);
4336 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4337 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4340 // Use SEH to write back the base address and the region size. In the case
4341 // of an exception, we strangely do return back the exception code, even
4342 // though the memory *has* been allocated. This mimics Windows behavior and
4343 // there is not much we can do about it.
4347 *URegionSize
= PRegionSize
;
4348 *UBaseAddress
= (PVOID
)StartingAddress
;
4350 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4352 Status
= _SEH2_GetExceptionCode();
4363 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4364 IN PVOID
* UBaseAddress
,
4365 IN PSIZE_T URegionSize
,
4368 PMEMORY_AREA MemoryArea
;
4371 ULONG_PTR CommitReduction
= 0;
4372 ULONG_PTR StartingAddress
, EndingAddress
;
4376 PMMSUPPORT AddressSpace
;
4377 PETHREAD CurrentThread
= PsGetCurrentThread();
4378 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4379 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4380 KAPC_STATE ApcState
;
4381 BOOLEAN Attached
= FALSE
;
4385 // Only two flags are supported
4387 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4389 DPRINT1("Invalid FreeType\n");
4390 return STATUS_INVALID_PARAMETER_4
;
4394 // Check if no flag was used, or if both flags were used
4396 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
4397 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
4399 DPRINT1("Invalid FreeType combination\n");
4400 return STATUS_INVALID_PARAMETER_4
;
4404 // Enter SEH for probe and capture. On failure, return back to the caller
4405 // with an exception violation.
4410 // Check for user-mode parameters and make sure that they are writeable
4412 if (PreviousMode
!= KernelMode
)
4414 ProbeForWritePointer(UBaseAddress
);
4415 ProbeForWriteUlong(URegionSize
);
4419 // Capture the current values
4421 PBaseAddress
= *UBaseAddress
;
4422 PRegionSize
= *URegionSize
;
4424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4426 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4431 // Make sure the allocation isn't past the user area
4433 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4435 DPRINT1("Virtual free base above User Space\n");
4436 return STATUS_INVALID_PARAMETER_2
;
4440 // Make sure the allocation wouldn't overflow past the user area
4442 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4444 DPRINT1("Region size would overflow into kernel-memory\n");
4445 return STATUS_INVALID_PARAMETER_3
;
4449 // If this is for the current process, just use PsGetCurrentProcess
4451 if (ProcessHandle
== NtCurrentProcess())
4453 Process
= CurrentProcess
;
4458 // Otherwise, reference the process with VM rights and attach to it if
4459 // this isn't the current process. We must attach because we'll be touching
4460 // PTEs and PDEs that belong to user-mode memory, and also touching the
4461 // Working Set which is stored in Hyperspace.
4463 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4464 PROCESS_VM_OPERATION
,
4469 if (!NT_SUCCESS(Status
)) return Status
;
4470 if (CurrentProcess
!= Process
)
4472 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4478 // Lock the address space
4480 AddressSpace
= MmGetCurrentAddressSpace();
4481 MmLockAddressSpace(AddressSpace
);
4484 // If the address space is being deleted, fail the de-allocation since it's
4485 // too late to do anything about it
4487 if (Process
->VmDeleted
)
4489 DPRINT1("Process is dead\n");
4490 Status
= STATUS_PROCESS_IS_TERMINATING
;
4495 // Compute start and end addresses, and locate the VAD
4497 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4498 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4499 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4502 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4503 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4508 // If the range exceeds the VAD's ending VPN, fail this request
4510 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4512 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4513 Status
= STATUS_UNABLE_TO_FREE_VM
;
4518 // Only private memory (except rotate VADs) can be freed through here */
4520 if ((!(Vad
->u
.VadFlags
.PrivateMemory
) &&
4521 (Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
)) ||
4522 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
))
4524 DPRINT1("Attempt to free section memory\n");
4525 Status
= STATUS_UNABLE_TO_DELETE_SECTION
;
4530 // ARM3 does not yet handle protected VM
4532 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4535 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4536 // and that is is an ARM3 memory area, and not a section view, as we currently
4537 // don't support freeing those though this interface.
4539 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4541 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4544 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4546 if (FreeType
& MEM_RELEASE
)
4549 // ARM3 only supports this VAD in this path
4551 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4554 // Is the caller trying to remove the whole VAD, or remove only a portion
4555 // of it? If no region size is specified, then the assumption is that the
4556 // whole VAD is to be destroyed
4561 // The caller must specify the base address identically to the range
4562 // that is stored in the VAD.
4564 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4566 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4567 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4572 // Now compute the actual start/end addresses based on the VAD
4574 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4575 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4578 // Finally lock the working set and remove the VAD from the VAD tree
4580 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4581 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4582 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4587 // This means the caller wants to release a specific region within
4588 // the range. We have to find out which range this is -- the following
4589 // possibilities exist plus their union (CASE D):
4591 // STARTING ADDRESS ENDING ADDRESS
4592 // [<========][========================================][=========>]
4593 // CASE A CASE B CASE C
4596 // First, check for case A or D
4598 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4603 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4606 // This is the easiest one to handle -- it is identical to
4607 // the code path above when the caller sets a zero region size
4608 // and the whole VAD is destroyed
4610 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4611 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4612 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4617 // This case is pretty easy too -- we compute a bunch of
4618 // pages to decommit, and then push the VAD's starting address
4619 // a bit further down, then decrement the commit charge
4621 // NOT YET IMPLEMENTED IN ARM3.
4623 DPRINT1("Case A not handled\n");
4624 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4628 // After analyzing the VAD, set it to NULL so that we don't
4629 // free it in the exit path
4637 // This is case B or case C. First check for case C
4639 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4641 PMEMORY_AREA MemoryArea
;
4644 // This is pretty easy and similar to case A. We compute the
4645 // amount of pages to decommit, update the VAD's commit charge
4646 // and then change the ending address of the VAD to be a bit
4649 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4650 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4654 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4655 // For ReactOS: shrink the corresponding memory area
4656 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4657 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
4658 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
4659 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4660 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
4665 // This is case B and the hardest one. Because we are removing
4666 // a chunk of memory from the very middle of the VAD, we must
4667 // actually split the VAD into two new VADs and compute the
4668 // commit charges for each of them, and reinsert new charges.
4670 // NOT YET IMPLEMENTED IN ARM3.
4672 DPRINT1("Case B not handled\n");
4673 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4678 // After analyzing the VAD, set it to NULL so that we don't
4679 // free it in the exit path
4686 // Now we have a range of pages to dereference, so call the right API
4687 // to do that and then release the working set, since we're done messing
4688 // around with process pages.
4690 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4691 MiUnlockWorkingSet(CurrentThread
, AddressSpace
);
4692 Status
= STATUS_SUCCESS
;
4696 // Update the process counters
4698 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4699 Process
->CommitCharge
-= CommitReduction
;
4700 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4703 // Unlock the address space and free the VAD in failure cases. Next,
4704 // detach from the target process so we can write the region size and the
4705 // base address to the correct source process, and dereference the target
4708 MmUnlockAddressSpace(AddressSpace
);
4709 if (Vad
) ExFreePool(Vad
);
4710 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4711 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4714 // Use SEH to safely return the region size and the base address of the
4715 // deallocation. If we get an access violation, don't return a failure code
4716 // as the deallocation *has* happened. The caller will just have to figure
4717 // out another way to find out where it is (such as VirtualQuery).
4721 *URegionSize
= PRegionSize
;
4722 *UBaseAddress
= (PVOID
)StartingAddress
;
4724 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4732 // This is the decommit path. You cannot decommit from the following VADs in
4733 // Windows, so fail the vall
4735 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4736 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4737 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4739 DPRINT1("Trying to decommit from invalid VAD\n");
4740 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4745 // If the caller did not specify a region size, first make sure that this
4746 // region is actually committed. If it is, then compute the ending address
4747 // based on the VAD.
4751 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4753 DPRINT1("Decomitting non-committed memory\n");
4754 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4757 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4761 // Decommit the PTEs for the range plus the actual backing pages for the
4762 // range, then reduce that amount from the commit charge in the VAD
4764 CommitReduction
= MiAddressToPte(EndingAddress
) -
4765 MiAddressToPte(StartingAddress
) +
4767 MiDecommitPages((PVOID
)StartingAddress
,
4768 MiAddressToPte(EndingAddress
),
4771 ASSERT(CommitReduction
>= 0);
4772 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4773 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4776 // We are done, go to the exit path without freeing the VAD as it remains
4777 // valid since we have not released the allocation.
4780 Status
= STATUS_SUCCESS
;
4784 // In the failure path, we detach and derefernece the target process, and
4785 // return whatever failure code was sent.
4788 MmUnlockAddressSpace(AddressSpace
);
4789 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4790 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);