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 MiProtectVirtualMemory(IN PEPROCESS Process
,
1755 IN OUT PVOID
*BaseAddress
,
1756 IN OUT PSIZE_T NumberOfBytesToProtect
,
1757 IN ULONG NewAccessProtection
,
1758 OUT PULONG OldAccessProtection OPTIONAL
)
1760 PMEMORY_AREA MemoryArea
;
1762 PMMSUPPORT AddressSpace
;
1763 ULONG_PTR StartingAddress
, EndingAddress
;
1764 PMMPTE PointerPde
, PointerPte
, LastPte
;
1766 PUSHORT UsedPageTableEntries
;
1768 ULONG ProtectionMask
;
1769 NTSTATUS Status
= STATUS_SUCCESS
;
1771 /* Check for ROS specific memory area */
1772 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1773 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1775 return MiRosProtectVirtualMemory(Process
,
1777 NumberOfBytesToProtect
,
1778 NewAccessProtection
,
1779 OldAccessProtection
);
1782 /* Calcualte base address for the VAD */
1783 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN((*BaseAddress
));
1784 EndingAddress
= (((ULONG_PTR
)*BaseAddress
+ *NumberOfBytesToProtect
- 1) | (PAGE_SIZE
- 1));
1786 /* Calculate the protection mask and make sure it's valid */
1787 ProtectionMask
= MiMakeProtectionMask(NewAccessProtection
);
1788 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1790 DPRINT1("Invalid protection mask\n");
1791 return STATUS_INVALID_PAGE_PROTECTION
;
1794 /* Lock the address space and make sure the process isn't already dead */
1795 AddressSpace
= MmGetCurrentAddressSpace();
1796 MmLockAddressSpace(AddressSpace
);
1797 if (Process
->VmDeleted
)
1799 DPRINT1("Process is dying\n");
1800 Status
= STATUS_PROCESS_IS_TERMINATING
;
1804 /* Get the VAD for this address range, and make sure it exists */
1805 Vad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
1806 EndingAddress
>> PAGE_SHIFT
,
1810 DPRINT("Could not find a VAD for this allocation\n");
1811 Status
= STATUS_CONFLICTING_ADDRESSES
;
1815 /* Make sure the address is within this VAD's boundaries */
1816 if ((((ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
) < Vad
->StartingVpn
) ||
1817 (((ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
) > Vad
->EndingVpn
))
1819 Status
= STATUS_CONFLICTING_ADDRESSES
;
1823 /* These kinds of VADs are not supported atm */
1824 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
1825 (Vad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
1826 (Vad
->u
.VadFlags
.VadType
== VadLargePages
))
1828 DPRINT1("Illegal VAD for attempting to set protection\n");
1829 Status
= STATUS_CONFLICTING_ADDRESSES
;
1833 /* Check for a VAD whose protection can't be changed */
1834 if (Vad
->u
.VadFlags
.NoChange
== 1)
1836 DPRINT1("Trying to change protection of a NoChange VAD\n");
1837 Status
= STATUS_INVALID_PAGE_PROTECTION
;
1841 if (Vad
->u
.VadFlags
.PrivateMemory
== 0)
1843 /* This is a section, handled by the ROS specific code above */
1848 /* Private memory, check protection flags */
1849 if ((NewAccessProtection
& PAGE_WRITECOPY
) ||
1850 (NewAccessProtection
& PAGE_EXECUTE_WRITECOPY
))
1852 Status
= STATUS_INVALID_PARAMETER_4
;
1856 //MiLockProcessWorkingSet(Thread, Process);
1858 /* TODO: Check if all pages in this range are committed */
1860 /* Compute starting and ending PTE and PDE addresses */
1861 PointerPde
= MiAddressToPde(StartingAddress
);
1862 PointerPte
= MiAddressToPte(StartingAddress
);
1863 LastPte
= MiAddressToPte(EndingAddress
);
1865 /* Make this PDE valid */
1866 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1868 /* Save protection of the first page */
1869 if (PointerPte
->u
.Long
!= 0)
1871 /* Capture the page protection and make the PDE valid */
1872 *OldAccessProtection
= MiGetPageProtection(PointerPte
);
1873 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1877 /* Grab the old protection from the VAD itself */
1878 *OldAccessProtection
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1881 /* Loop all the PTEs now */
1882 while (PointerPte
<= LastPte
)
1884 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
1885 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
1887 PointerPde
= MiAddressToPte(PointerPte
);
1888 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1891 /* Capture the PTE and see what we're dealing with */
1892 PteContents
= *PointerPte
;
1893 if (PteContents
.u
.Long
== 0)
1895 /* This used to be a zero PTE and it no longer is, so we must add a
1896 reference to the pagetable. */
1897 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
1898 (*UsedPageTableEntries
)++;
1899 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
1901 else if (PteContents
.u
.Hard
.Valid
== 1)
1903 /* Get the PFN entry */
1904 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
1906 /* We don't support this yet */
1907 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
1909 /* Check if the page should not be accessible at all */
1910 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
1911 (NewAccessProtection
& PAGE_GUARD
))
1917 /* Write the protection mask and write it with a TLB flush */
1918 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
1919 MiFlushTbAndCapture(Vad
,
1927 /* We don't support these cases yet */
1928 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
1929 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
1931 /* The PTE is already demand-zero, just update the protection mask */
1932 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
1938 /* Unlock the working set and update quota charges if needed, then return */
1939 //MiUnlockProcessWorkingSet(Thread, Process);
1943 /* Unlock the address space */
1944 MmUnlockAddressSpace(AddressSpace
);
1946 /* Return parameters */
1947 *NumberOfBytesToProtect
= (SIZE_T
)((PUCHAR
)EndingAddress
- (PUCHAR
)StartingAddress
+ 1);
1948 *BaseAddress
= (PVOID
)StartingAddress
;
1955 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
1956 IN PEPROCESS TargetProcess
,
1959 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
1962 // Sanity checks. The latter is because we only use this function with the
1963 // PFN lock not held, so it may go away in the future.
1965 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1966 ASSERT(OldIrql
== MM_NOIRQL
);
1969 // Also get the PPE and PXE. This is okay not to #ifdef because they will
1970 // return the same address as the PDE on 2-level page table systems.
1972 // If everything is already valid, there is nothing to do.
1974 PointerPpe
= MiAddressToPte(PointerPde
);
1975 PointerPxe
= MiAddressToPde(PointerPde
);
1976 if ((PointerPxe
->u
.Hard
.Valid
) &&
1977 (PointerPpe
->u
.Hard
.Valid
) &&
1978 (PointerPde
->u
.Hard
.Valid
))
1984 // At least something is invalid, so begin by getting the PTE for the PDE itself
1985 // and then lookup each additional level. We must do it in this precise order
1986 // because the pagfault.c code (as well as in Windows) depends that the next
1987 // level up (higher) must be valid when faulting a lower level
1989 PointerPte
= MiPteToAddress(PointerPde
);
1993 // Make sure APCs continued to be disabled
1995 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1998 // First, make the PXE valid if needed
2000 if (!PointerPxe
->u
.Hard
.Valid
)
2002 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2003 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2009 if (!PointerPpe
->u
.Hard
.Valid
)
2011 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2012 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2016 // And finally, make the PDE itself valid.
2018 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2021 // This should've worked the first time so the loop is really just for
2022 // show -- ASSERT that we're actually NOT going to be looping.
2024 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2025 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2026 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2027 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2028 !(PointerPpe
->u
.Hard
.Valid
) ||
2029 !(PointerPde
->u
.Hard
.Valid
));
2034 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2040 PFN_NUMBER PageFrameIndex
;
2044 // Acquire the PFN lock and loop all the PTEs in the list
2046 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2047 for (i
= 0; i
!= Count
; i
++)
2050 // The PTE must currently be valid
2052 TempPte
= *ValidPteList
[i
];
2053 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2056 // Get the PFN entry for the page itself, and then for its page table
2058 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2059 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2060 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2063 // Decrement the share count on the page table, and then on the page
2066 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2067 MI_SET_PFN_DELETED(Pfn1
);
2068 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2071 // Make the page decommitted
2073 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2077 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2078 // and then release the PFN lock
2081 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2086 MiDecommitPages(IN PVOID StartingAddress
,
2087 IN PMMPTE EndingPte
,
2088 IN PEPROCESS Process
,
2091 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2092 ULONG CommitReduction
= 0;
2093 PMMPTE ValidPteList
[256];
2097 PUSHORT UsedPageTableEntries
;
2098 PETHREAD CurrentThread
= PsGetCurrentThread();
2101 // Get the PTE and PTE for the address, and lock the working set
2102 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2103 // commited range ends so that we can do the right accounting.
2105 PointerPde
= MiAddressToPde(StartingAddress
);
2106 PointerPte
= MiAddressToPte(StartingAddress
);
2107 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2108 MiLockWorkingSet(CurrentThread
, &Process
->Vm
);
2111 // Make the PDE valid, and now loop through each page's worth of data
2113 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2114 while (PointerPte
<= EndingPte
)
2117 // Check if we've crossed a PDE boundary
2119 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
2122 // Get the new PDE and flush the valid PTEs we had built up until
2123 // now. This helps reduce the amount of TLB flushing we have to do.
2124 // Note that Windows does a much better job using timestamps and
2125 // such, and does not flush the entire TLB all the time, but right
2126 // now we have bigger problems to worry about than TLB flushing.
2128 PointerPde
= MiAddressToPde(StartingAddress
);
2131 MiProcessValidPteList(ValidPteList
, PteCount
);
2136 // Make this PDE valid
2138 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2142 // Read this PTE. It might be active or still demand-zero.
2144 PteContents
= *PointerPte
;
2145 if (PteContents
.u
.Long
)
2148 // The PTE is active. It might be valid and in a working set, or
2149 // it might be a prototype PTE or paged out or even in transition.
2151 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2154 // It's already decommited, so there's nothing for us to do here
2161 // Remove it from the counters, and check if it was valid or not
2163 //Process->NumberOfPrivatePages--;
2164 if (PteContents
.u
.Hard
.Valid
)
2167 // It's valid. At this point make sure that it is not a ROS
2168 // PFN. Also, we don't support ProtoPTEs in this code path.
2170 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2171 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2172 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2175 // Flush any pending PTEs that we had not yet flushed, if our
2176 // list has gotten too big, then add this PTE to the flush list.
2178 if (PteCount
== 256)
2180 MiProcessValidPteList(ValidPteList
, PteCount
);
2183 ValidPteList
[PteCount
++] = PointerPte
;
2188 // We do not support any of these other scenarios at the moment
2190 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2191 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2192 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2195 // So the only other possibility is that it is still a demand
2196 // zero PTE, in which case we undo the accounting we did
2197 // earlier and simply make the page decommitted.
2199 //Process->NumberOfPrivatePages++;
2200 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2207 // This used to be a zero PTE and it no longer is, so we must add a
2208 // reference to the pagetable.
2210 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(StartingAddress
)];
2211 (*UsedPageTableEntries
)++;
2212 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
2215 // Next, we account for decommitted PTEs and make the PTE as such
2217 if (PointerPte
> CommitPte
) CommitReduction
++;
2218 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2222 // Move to the next PTE and the next address
2225 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2229 // Flush any dangling PTEs from the loop in the last page table, and then
2230 // release the working set and return the commit reduction accounting.
2232 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2233 MiUnlockWorkingSet(CurrentThread
, &Process
->Vm
);
2234 return CommitReduction
;
2237 /* PUBLIC FUNCTIONS ***********************************************************/
2244 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2255 MmSecureVirtualMemory(IN PVOID Address
,
2259 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2268 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2270 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2273 /* SYSTEM CALLS ***************************************************************/
2277 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2278 IN PVOID BaseAddress
,
2280 IN SIZE_T NumberOfBytesToRead
,
2281 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2283 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2285 NTSTATUS Status
= STATUS_SUCCESS
;
2286 SIZE_T BytesRead
= 0;
2290 // Check if we came from user mode
2292 if (PreviousMode
!= KernelMode
)
2295 // Validate the read addresses
2297 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2298 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2299 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2300 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2303 // Don't allow to write into kernel space
2305 return STATUS_ACCESS_VIOLATION
;
2309 // Enter SEH for probe
2314 // Probe the output value
2316 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2318 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2321 // Get exception code
2323 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2329 // Don't do zero-byte transfers
2331 if (NumberOfBytesToRead
)
2334 // Reference the process
2336 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2342 if (NT_SUCCESS(Status
))
2347 Status
= MmCopyVirtualMemory(Process
,
2349 PsGetCurrentProcess(),
2351 NumberOfBytesToRead
,
2356 // Dereference the process
2358 ObDereferenceObject(Process
);
2363 // Check if the caller sent this parameter
2365 if (NumberOfBytesRead
)
2368 // Enter SEH to guard write
2373 // Return the number of bytes read
2375 *NumberOfBytesRead
= BytesRead
;
2377 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2391 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2392 IN PVOID BaseAddress
,
2394 IN SIZE_T NumberOfBytesToWrite
,
2395 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2397 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2399 NTSTATUS Status
= STATUS_SUCCESS
;
2400 SIZE_T BytesWritten
= 0;
2404 // Check if we came from user mode
2406 if (PreviousMode
!= KernelMode
)
2409 // Validate the read addresses
2411 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2412 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2413 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2414 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2417 // Don't allow to write into kernel space
2419 return STATUS_ACCESS_VIOLATION
;
2423 // Enter SEH for probe
2428 // Probe the output value
2430 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2432 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2435 // Get exception code
2437 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2443 // Don't do zero-byte transfers
2445 if (NumberOfBytesToWrite
)
2448 // Reference the process
2450 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2456 if (NT_SUCCESS(Status
))
2461 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2465 NumberOfBytesToWrite
,
2470 // Dereference the process
2472 ObDereferenceObject(Process
);
2477 // Check if the caller sent this parameter
2479 if (NumberOfBytesWritten
)
2482 // Enter SEH to guard write
2487 // Return the number of bytes written
2489 *NumberOfBytesWritten
= BytesWritten
;
2491 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2505 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2506 IN OUT PVOID
*UnsafeBaseAddress
,
2507 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2508 IN ULONG NewAccessProtection
,
2509 OUT PULONG UnsafeOldAccessProtection
)
2512 ULONG OldAccessProtection
;
2514 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2515 PVOID BaseAddress
= NULL
;
2516 SIZE_T NumberOfBytesToProtect
= 0;
2517 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2519 BOOLEAN Attached
= FALSE
;
2520 KAPC_STATE ApcState
;
2524 // Check for valid protection flags
2526 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2527 if (Protection
!= PAGE_NOACCESS
&&
2528 Protection
!= PAGE_READONLY
&&
2529 Protection
!= PAGE_READWRITE
&&
2530 Protection
!= PAGE_WRITECOPY
&&
2531 Protection
!= PAGE_EXECUTE
&&
2532 Protection
!= PAGE_EXECUTE_READ
&&
2533 Protection
!= PAGE_EXECUTE_READWRITE
&&
2534 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2539 return STATUS_INVALID_PAGE_PROTECTION
;
2543 // Check if we came from user mode
2545 if (PreviousMode
!= KernelMode
)
2548 // Enter SEH for probing
2553 // Validate all outputs
2555 ProbeForWritePointer(UnsafeBaseAddress
);
2556 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2557 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2562 BaseAddress
= *UnsafeBaseAddress
;
2563 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2565 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2568 // Get exception code
2570 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2579 BaseAddress
= *UnsafeBaseAddress
;
2580 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2584 // Catch illegal base address
2586 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2589 // Catch illegal region size
2591 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2596 return STATUS_INVALID_PARAMETER_3
;
2600 // 0 is also illegal
2602 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2605 // Get a reference to the process
2607 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2608 PROCESS_VM_OPERATION
,
2613 if (!NT_SUCCESS(Status
)) return Status
;
2616 // Check if we should attach
2618 if (CurrentProcess
!= Process
)
2623 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2628 // Do the actual work
2630 Status
= MiProtectVirtualMemory(Process
,
2632 &NumberOfBytesToProtect
,
2633 NewAccessProtection
,
2634 &OldAccessProtection
);
2639 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2642 // Release reference
2644 ObDereferenceObject(Process
);
2647 // Enter SEH to return data
2652 // Return data to user
2654 *UnsafeOldAccessProtection
= OldAccessProtection
;
2655 *UnsafeBaseAddress
= BaseAddress
;
2656 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2658 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2671 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2672 IN OUT PVOID
*BaseAddress
,
2673 IN OUT PSIZE_T NumberOfBytesToLock
,
2677 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2679 BOOLEAN Attached
= FALSE
;
2680 KAPC_STATE ApcState
;
2681 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2682 PVOID CapturedBaseAddress
;
2683 SIZE_T CapturedBytesToLock
;
2689 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2692 // Invalid set of flags
2694 return STATUS_INVALID_PARAMETER
;
2698 // At least one flag must be specified
2700 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2705 return STATUS_INVALID_PARAMETER
;
2709 // Enter SEH for probing
2714 // Validate output data
2716 ProbeForWritePointer(BaseAddress
);
2717 ProbeForWriteSize_t(NumberOfBytesToLock
);
2722 CapturedBaseAddress
= *BaseAddress
;
2723 CapturedBytesToLock
= *NumberOfBytesToLock
;
2725 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2728 // Get exception code
2730 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2735 // Catch illegal base address
2737 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2740 // Catch illegal region size
2742 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2747 return STATUS_INVALID_PARAMETER
;
2751 // 0 is also illegal
2753 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2756 // Get a reference to the process
2758 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2759 PROCESS_VM_OPERATION
,
2764 if (!NT_SUCCESS(Status
)) return Status
;
2767 // Check if this is is system-mapped
2769 if (MapType
& MAP_SYSTEM
)
2772 // Check for required privilege
2774 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2777 // Fail: Don't have it
2779 ObDereferenceObject(Process
);
2780 return STATUS_PRIVILEGE_NOT_HELD
;
2785 // Check if we should attach
2787 if (CurrentProcess
!= Process
)
2792 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2804 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2807 // Release reference
2809 ObDereferenceObject(Process
);
2812 // Enter SEH to return data
2817 // Return data to user
2819 *BaseAddress
= CapturedBaseAddress
;
2820 *NumberOfBytesToLock
= 0;
2822 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2825 // Get exception code
2827 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2834 return STATUS_SUCCESS
;
2839 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
2840 IN OUT PVOID
*BaseAddress
,
2841 IN OUT PSIZE_T NumberOfBytesToUnlock
,
2845 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2847 BOOLEAN Attached
= FALSE
;
2848 KAPC_STATE ApcState
;
2849 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2850 PVOID CapturedBaseAddress
;
2851 SIZE_T CapturedBytesToUnlock
;
2857 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2860 // Invalid set of flags
2862 return STATUS_INVALID_PARAMETER
;
2866 // At least one flag must be specified
2868 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2873 return STATUS_INVALID_PARAMETER
;
2877 // Enter SEH for probing
2882 // Validate output data
2884 ProbeForWritePointer(BaseAddress
);
2885 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
2890 CapturedBaseAddress
= *BaseAddress
;
2891 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
2893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2896 // Get exception code
2898 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2903 // Catch illegal base address
2905 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2908 // Catch illegal region size
2910 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
2915 return STATUS_INVALID_PARAMETER
;
2919 // 0 is also illegal
2921 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
2924 // Get a reference to the process
2926 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2927 PROCESS_VM_OPERATION
,
2932 if (!NT_SUCCESS(Status
)) return Status
;
2935 // Check if this is is system-mapped
2937 if (MapType
& MAP_SYSTEM
)
2940 // Check for required privilege
2942 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2945 // Fail: Don't have it
2947 ObDereferenceObject(Process
);
2948 return STATUS_PRIVILEGE_NOT_HELD
;
2953 // Check if we should attach
2955 if (CurrentProcess
!= Process
)
2960 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2972 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2975 // Release reference
2977 ObDereferenceObject(Process
);
2980 // Enter SEH to return data
2985 // Return data to user
2987 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2988 *NumberOfBytesToUnlock
= 0;
2990 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2993 // Get exception code
2995 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3002 return STATUS_SUCCESS
;
3007 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3008 IN OUT PVOID
*BaseAddress
,
3009 IN OUT PSIZE_T NumberOfBytesToFlush
,
3010 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3014 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3015 PVOID CapturedBaseAddress
;
3016 SIZE_T CapturedBytesToFlush
;
3017 IO_STATUS_BLOCK LocalStatusBlock
;
3021 // Check if we came from user mode
3023 if (PreviousMode
!= KernelMode
)
3026 // Enter SEH for probing
3031 // Validate all outputs
3033 ProbeForWritePointer(BaseAddress
);
3034 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3035 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3040 CapturedBaseAddress
= *BaseAddress
;
3041 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3043 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3046 // Get exception code
3048 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3057 CapturedBaseAddress
= *BaseAddress
;
3058 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3062 // Catch illegal base address
3064 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3067 // Catch illegal region size
3069 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3074 return STATUS_INVALID_PARAMETER
;
3078 // Get a reference to the process
3080 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3081 PROCESS_VM_OPERATION
,
3086 if (!NT_SUCCESS(Status
)) return Status
;
3091 Status
= MmFlushVirtualMemory(Process
,
3092 &CapturedBaseAddress
,
3093 &CapturedBytesToFlush
,
3097 // Release reference
3099 ObDereferenceObject(Process
);
3102 // Enter SEH to return data
3107 // Return data to user
3109 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3110 *NumberOfBytesToFlush
= 0;
3111 *IoStatusBlock
= LocalStatusBlock
;
3113 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3129 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3131 IN PVOID BaseAddress
,
3132 IN SIZE_T RegionSize
,
3133 IN PVOID
*UserAddressArray
,
3134 OUT PULONG_PTR EntriesInUserAddressArray
,
3135 OUT PULONG Granularity
)
3140 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3141 ULONG_PTR CapturedEntryCount
;
3145 // Check if we came from user mode
3147 if (PreviousMode
!= KernelMode
)
3150 // Enter SEH for probing
3155 // Catch illegal base address
3157 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3160 // Catch illegal region size
3162 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3167 return STATUS_INVALID_PARAMETER_3
;
3171 // Validate all data
3173 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3174 ProbeForWriteUlong(Granularity
);
3179 CapturedEntryCount
= *EntriesInUserAddressArray
;
3182 // Must have a count
3184 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
3187 // Can't be larger than the maximum
3189 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3194 return STATUS_INVALID_PARAMETER_5
;
3198 // Probe the actual array
3200 ProbeForWrite(UserAddressArray
,
3201 CapturedEntryCount
* sizeof(PVOID
),
3204 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3207 // Get exception code
3209 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3218 CapturedEntryCount
= *EntriesInUserAddressArray
;
3219 ASSERT(CapturedEntryCount
!= 0);
3223 // Check if this is a local request
3225 if (ProcessHandle
== NtCurrentProcess())
3228 // No need to reference the process
3230 Process
= PsGetCurrentProcess();
3235 // Reference the target
3237 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3238 PROCESS_VM_OPERATION
,
3243 if (!NT_SUCCESS(Status
)) return Status
;
3247 // Compute the last address and validate it
3249 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3250 if (BaseAddress
> EndAddress
)
3255 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3256 return STATUS_INVALID_PARAMETER_4
;
3265 // Dereference if needed
3267 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3270 // Enter SEH to return data
3275 // Return data to user
3277 *EntriesInUserAddressArray
= 0;
3278 *Granularity
= PAGE_SIZE
;
3280 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3283 // Get exception code
3285 Status
= _SEH2_GetExceptionCode();
3292 return STATUS_SUCCESS
;
3300 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3301 IN PVOID BaseAddress
,
3302 IN SIZE_T RegionSize
)
3307 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3308 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3311 // Catch illegal base address
3313 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3316 // Catch illegal region size
3318 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3323 return STATUS_INVALID_PARAMETER_3
;
3327 // Check if this is a local request
3329 if (ProcessHandle
== NtCurrentProcess())
3332 // No need to reference the process
3334 Process
= PsGetCurrentProcess();
3339 // Reference the target
3341 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3342 PROCESS_VM_OPERATION
,
3347 if (!NT_SUCCESS(Status
)) return Status
;
3351 // Compute the last address and validate it
3353 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3354 if (BaseAddress
> EndAddress
)
3359 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3360 return STATUS_INVALID_PARAMETER_3
;
3369 // Dereference if needed
3371 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3376 return STATUS_SUCCESS
;
3381 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3382 IN PVOID BaseAddress
,
3383 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3384 OUT PVOID MemoryInformation
,
3385 IN SIZE_T MemoryInformationLength
,
3386 OUT PSIZE_T ReturnLength
)
3388 NTSTATUS Status
= STATUS_SUCCESS
;
3389 KPROCESSOR_MODE PreviousMode
;
3391 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3393 /* Bail out if the address is invalid */
3394 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3396 /* Probe return buffer */
3397 PreviousMode
= ExGetPreviousMode();
3398 if (PreviousMode
!= KernelMode
)
3402 ProbeForWrite(MemoryInformation
,
3403 MemoryInformationLength
,
3406 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3408 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3410 Status
= _SEH2_GetExceptionCode();
3414 if (!NT_SUCCESS(Status
))
3420 switch(MemoryInformationClass
)
3422 case MemoryBasicInformation
:
3423 /* Validate the size information of the class */
3424 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3426 /* The size is invalid */
3427 return STATUS_INFO_LENGTH_MISMATCH
;
3429 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3432 MemoryInformationLength
,
3436 case MemorySectionName
:
3437 /* Validate the size information of the class */
3438 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3440 /* The size is invalid */
3441 return STATUS_INFO_LENGTH_MISMATCH
;
3443 Status
= MiQueryMemorySectionName(ProcessHandle
,
3446 MemoryInformationLength
,
3449 case MemoryWorkingSetList
:
3450 case MemoryBasicVlmInformation
:
3452 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3464 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3465 IN OUT PVOID
* UBaseAddress
,
3466 IN ULONG_PTR ZeroBits
,
3467 IN OUT PSIZE_T URegionSize
,
3468 IN ULONG AllocationType
,
3472 PMEMORY_AREA MemoryArea
;
3473 PFN_NUMBER PageCount
;
3474 PMMVAD Vad
, FoundVad
;
3475 PUSHORT UsedPageTableEntries
;
3477 PMMSUPPORT AddressSpace
;
3479 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3480 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3481 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3482 PETHREAD CurrentThread
= PsGetCurrentThread();
3483 KAPC_STATE ApcState
;
3484 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3485 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3487 PMMPTE PointerPte
, PointerPde
, LastPte
;
3490 /* Check for valid Zero bits */
3493 DPRINT1("Too many zero bits\n");
3494 return STATUS_INVALID_PARAMETER_3
;
3497 /* Check for valid Allocation Types */
3498 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3499 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3501 DPRINT1("Invalid Allocation Type\n");
3502 return STATUS_INVALID_PARAMETER_5
;
3505 /* Check for at least one of these Allocation Types to be set */
3506 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3508 DPRINT1("No memory allocation base type\n");
3509 return STATUS_INVALID_PARAMETER_5
;
3512 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3513 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3515 DPRINT1("Invalid use of MEM_RESET\n");
3516 return STATUS_INVALID_PARAMETER_5
;
3519 /* Check if large pages are being used */
3520 if (AllocationType
& MEM_LARGE_PAGES
)
3522 /* Large page allocations MUST be committed */
3523 if (!(AllocationType
& MEM_COMMIT
))
3525 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3526 return STATUS_INVALID_PARAMETER_5
;
3529 /* These flags are not allowed with large page allocations */
3530 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3532 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3533 return STATUS_INVALID_PARAMETER_5
;
3537 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3538 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3540 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3541 return STATUS_INVALID_PARAMETER_5
;
3544 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3545 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3547 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3548 return STATUS_INVALID_PARAMETER_5
;
3551 /* Check for valid MEM_PHYSICAL usage */
3552 if (AllocationType
& MEM_PHYSICAL
)
3554 /* Only these flags are allowed with MEM_PHYSIAL */
3555 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3557 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3558 return STATUS_INVALID_PARAMETER_5
;
3561 /* Then make sure PAGE_READWRITE is used */
3562 if (Protect
!= PAGE_READWRITE
)
3564 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3565 return STATUS_INVALID_PARAMETER_6
;
3570 // Force PAGE_READWRITE for everything, for now
3572 Protect
= PAGE_READWRITE
;
3574 /* Calculate the protection mask and make sure it's valid */
3575 ProtectionMask
= MiMakeProtectionMask(Protect
);
3576 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3578 DPRINT1("Invalid protection mask\n");
3579 return STATUS_INVALID_PAGE_PROTECTION
;
3585 /* Check for user-mode parameters */
3586 if (PreviousMode
!= KernelMode
)
3588 /* Make sure they are writable */
3589 ProbeForWritePointer(UBaseAddress
);
3590 ProbeForWriteUlong(URegionSize
);
3593 /* Capture their values */
3594 PBaseAddress
= *UBaseAddress
;
3595 PRegionSize
= *URegionSize
;
3597 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3599 /* Return the exception code */
3600 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3604 /* Make sure the allocation isn't past the VAD area */
3605 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3607 DPRINT1("Virtual allocation base above User Space\n");
3608 return STATUS_INVALID_PARAMETER_2
;
3611 /* Make sure the allocation wouldn't overflow past the VAD area */
3612 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3614 DPRINT1("Region size would overflow into kernel-memory\n");
3615 return STATUS_INVALID_PARAMETER_4
;
3618 /* Make sure there's a size specified */
3621 DPRINT1("Region size is invalid (zero)\n");
3622 return STATUS_INVALID_PARAMETER_4
;
3626 // If this is for the current process, just use PsGetCurrentProcess
3628 if (ProcessHandle
== NtCurrentProcess())
3630 Process
= CurrentProcess
;
3635 // Otherwise, reference the process with VM rights and attach to it if
3636 // this isn't the current process. We must attach because we'll be touching
3637 // PTEs and PDEs that belong to user-mode memory, and also touching the
3638 // Working Set which is stored in Hyperspace.
3640 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3641 PROCESS_VM_OPERATION
,
3646 if (!NT_SUCCESS(Status
)) return Status
;
3647 if (CurrentProcess
!= Process
)
3649 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3655 // Check for large page allocations and make sure that the required privilege
3656 // is being held, before attempting to handle them.
3658 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3659 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3661 /* Fail without it */
3662 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3663 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3664 goto FailPathNoLock
;
3668 // Assert on the things we don't yet support
3670 ASSERT(ZeroBits
== 0);
3671 ASSERT((AllocationType
& MEM_LARGE_PAGES
) == 0);
3672 ASSERT((AllocationType
& MEM_PHYSICAL
) == 0);
3673 ASSERT((AllocationType
& MEM_WRITE_WATCH
) == 0);
3674 ASSERT((AllocationType
& MEM_TOP_DOWN
) == 0);
3675 ASSERT((AllocationType
& MEM_RESET
) == 0);
3676 ASSERT(Process
->VmTopDown
== 0);
3679 // Check if the caller is reserving memory, or committing memory and letting
3680 // us pick the base address
3682 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3685 // Do not allow COPY_ON_WRITE through this API
3687 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3689 DPRINT1("Copy on write not allowed through this path\n");
3690 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3691 goto FailPathNoLock
;
3695 // Does the caller have an address in mind, or is this a blind commit?
3700 // This is a blind commit, all we need is the region size
3702 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3703 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3705 StartingAddress
= 0;
3710 // This is a reservation, so compute the starting address on the
3711 // expected 64KB granularity, and see where the ending address will
3712 // fall based on the aligned address and the passed in region size
3714 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3715 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3716 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3720 // Allocate and initialize the VAD
3722 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3723 ASSERT(Vad
!= NULL
);
3724 Vad
->u
.LongFlags
= 0;
3725 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3726 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3727 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3728 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3731 // Lock the address space and make sure the process isn't already dead
3733 AddressSpace
= MmGetCurrentAddressSpace();
3734 MmLockAddressSpace(AddressSpace
);
3735 if (Process
->VmDeleted
)
3737 Status
= STATUS_PROCESS_IS_TERMINATING
;
3742 // Did we have a base address? If no, find a valid address that is 64KB
3743 // aligned in the VAD tree. Otherwise, make sure that the address range
3744 // which was passed in isn't already conflicting with an existing address
3749 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3752 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3754 if (!NT_SUCCESS(Status
)) goto FailPath
;
3757 // Now we know where the allocation ends. Make sure it doesn't end up
3758 // somewhere in kernel mode.
3760 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3761 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3763 Status
= STATUS_NO_MEMORY
;
3767 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3768 EndingAddress
>> PAGE_SHIFT
,
3772 // The address specified is in conflict!
3774 Status
= STATUS_CONFLICTING_ADDRESSES
;
3779 // Write out the VAD fields for this allocation
3781 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3782 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3785 // FIXME: Should setup VAD bitmap
3787 Status
= STATUS_SUCCESS
;
3790 // Lock the working set and insert the VAD into the process VAD tree
3792 MiLockProcessWorkingSet(Process
, CurrentThread
);
3793 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3794 MiInsertVad(Vad
, Process
);
3795 MiUnlockProcessWorkingSet(Process
, CurrentThread
);
3798 // Update the virtual size of the process, and if this is now the highest
3799 // virtual size we have ever seen, update the peak virtual size to reflect
3802 Process
->VirtualSize
+= PRegionSize
;
3803 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
3805 Process
->PeakVirtualSize
= Process
->VirtualSize
;
3809 // Release address space and detach and dereference the target process if
3810 // it was different from the current process
3812 MmUnlockAddressSpace(AddressSpace
);
3813 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3814 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3817 // Use SEH to write back the base address and the region size. In the case
3818 // of an exception, we do not return back the exception code, as the memory
3819 // *has* been allocated. The caller would now have to call VirtualQuery
3820 // or do some other similar trick to actually find out where its memory
3821 // allocation ended up
3825 *URegionSize
= PRegionSize
;
3826 *UBaseAddress
= (PVOID
)StartingAddress
;
3828 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3832 return STATUS_SUCCESS
;
3836 // This is a MEM_COMMIT on top of an existing address which must have been
3837 // MEM_RESERVED already. Compute the start and ending base addresses based
3838 // on the user input, and then compute the actual region size once all the
3839 // alignments have been done.
3841 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
3842 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
3843 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
3846 // Lock the address space and make sure the process isn't already dead
3848 AddressSpace
= MmGetCurrentAddressSpace();
3849 MmLockAddressSpace(AddressSpace
);
3850 if (Process
->VmDeleted
)
3852 DPRINT1("Process is dying\n");
3853 Status
= STATUS_PROCESS_IS_TERMINATING
;
3858 // Get the VAD for this address range, and make sure it exists
3860 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3861 EndingAddress
>> PAGE_SHIFT
,
3865 DPRINT1("Could not find a VAD for this allocation\n");
3866 Status
= STATUS_CONFLICTING_ADDRESSES
;
3871 // These kinds of VADs are illegal for this Windows function when trying to
3872 // commit an existing range
3874 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
3875 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
3876 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
3878 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
3879 Status
= STATUS_CONFLICTING_ADDRESSES
;
3884 // Make sure that this address range actually fits within the VAD for it
3886 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
3887 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
3889 DPRINT1("Address range does not fit into the VAD\n");
3890 Status
= STATUS_CONFLICTING_ADDRESSES
;
3895 // If this is an existing section view, we call the old RosMm routine which
3896 // has the relevant code required to handle the section scenario. In the future
3897 // we will limit this even more so that there's almost nothing that the code
3898 // needs to do, and it will become part of section.c in RosMm
3900 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
3901 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
3903 return MiRosAllocateVirtualMemory(ProcessHandle
,
3914 // Is this a previously reserved section being committed? If so, enter the
3915 // special section path
3917 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
3920 // You cannot commit large page sections through this API
3922 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
3924 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
3925 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3930 // You can only use caching flags on a rotate VAD
3932 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
3933 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
3935 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
3936 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3941 // We should make sure that the section's permissions aren't being messed with
3943 if (FoundVad
->u
.VadFlags
.NoChange
)
3945 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
3949 // ARM3 does not support file-backed sections, only shared memory
3951 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
3954 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
3956 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
3957 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
3959 DPRINT1("Invalid page protection for rotate VAD\n");
3960 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3965 // Compute PTE addresses and the quota charge, then grab the commit lock
3967 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
3968 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
3969 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
3970 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
3973 // Get the segment template PTE and start looping each page
3975 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
3976 ASSERT(TempPte
.u
.Long
!= 0);
3977 while (PointerPte
<= LastPte
)
3980 // For each non-already-committed page, write the invalid template PTE
3982 if (PointerPte
->u
.Long
== 0)
3984 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3994 // Now do the commit accounting and release the lock
3996 ASSERT(QuotaCharge
>= QuotaFree
);
3997 QuotaCharge
-= QuotaFree
;
3998 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
3999 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4002 // We are done with committing the section pages
4004 Status
= STATUS_SUCCESS
;
4009 // This is a specific ReactOS check because we only use normal VADs
4011 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4014 // While this is an actual Windows check
4016 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4019 // Throw out attempts to use copy-on-write through this API path
4021 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4023 DPRINT1("Write copy attempted when not allowed\n");
4024 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4029 // Initialize a demand-zero PTE
4032 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4035 // Get the PTE, PDE and the last PTE for this address range
4037 PointerPde
= MiAddressToPde(StartingAddress
);
4038 PointerPte
= MiAddressToPte(StartingAddress
);
4039 LastPte
= MiAddressToPte(EndingAddress
);
4042 // Update the commit charge in the VAD as well as in the process, and check
4043 // if this commit charge was now higher than the last recorded peak, in which
4044 // case we also update the peak
4046 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4047 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4048 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4050 Process
->CommitChargePeak
= Process
->CommitCharge
;
4054 // Lock the working set while we play with user pages and page tables
4056 //MiLockWorkingSet(CurrentThread, AddressSpace);
4059 // Make the current page table valid, and then loop each page within it
4061 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4062 while (PointerPte
<= LastPte
)
4065 // Have we crossed into a new page table?
4067 if (!(((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)))
4070 // Get the PDE and now make it valid too
4072 PointerPde
= MiAddressToPte(PointerPte
);
4073 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4077 // Is this a zero PTE as expected?
4079 if (PointerPte
->u
.Long
== 0)
4082 // First increment the count of pages in the page table for this
4085 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
4086 (*UsedPageTableEntries
)++;
4087 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
4090 // And now write the invalid demand-zero PTE as requested
4092 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4094 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4097 // If the PTE was already decommitted, there is nothing else to do
4098 // but to write the new demand-zero PTE
4100 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4102 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4105 // We don't handle these scenarios yet
4107 if (PointerPte
->u
.Soft
.Valid
== 0)
4109 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4110 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4114 // There's a change in protection, remember this for later, but do
4115 // not yet handle it.
4117 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect
);
4118 ChangeProtection
= TRUE
;
4122 // Move to the next PTE
4128 // This path is not yet handled
4130 ASSERT(ChangeProtection
== FALSE
);
4133 // Release the working set lock, unlock the address space, and detach from
4134 // the target process if it was not the current process. Also dereference the
4135 // target process if this wasn't the case.
4137 //MiUnlockProcessWorkingSet(Process, CurrentThread);
4138 Status
= STATUS_SUCCESS
;
4140 MmUnlockAddressSpace(AddressSpace
);
4142 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4143 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4146 // Use SEH to write back the base address and the region size. In the case
4147 // of an exception, we strangely do return back the exception code, even
4148 // though the memory *has* been allocated. This mimics Windows behavior and
4149 // there is not much we can do about it.
4153 *URegionSize
= PRegionSize
;
4154 *UBaseAddress
= (PVOID
)StartingAddress
;
4156 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4158 Status
= _SEH2_GetExceptionCode();
4169 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4170 IN PVOID
* UBaseAddress
,
4171 IN PSIZE_T URegionSize
,
4174 PMEMORY_AREA MemoryArea
;
4177 ULONG_PTR CommitReduction
= 0;
4178 ULONG_PTR StartingAddress
, EndingAddress
;
4182 PMMSUPPORT AddressSpace
;
4183 PETHREAD CurrentThread
= PsGetCurrentThread();
4184 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4185 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4186 KAPC_STATE ApcState
;
4187 BOOLEAN Attached
= FALSE
;
4191 // Only two flags are supported
4193 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4195 DPRINT1("Invalid FreeType\n");
4196 return STATUS_INVALID_PARAMETER_4
;
4200 // Check if no flag was used, or if both flags were used
4202 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
4203 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
4205 DPRINT1("Invalid FreeType combination\n");
4206 return STATUS_INVALID_PARAMETER_4
;
4210 // Enter SEH for probe and capture. On failure, return back to the caller
4211 // with an exception violation.
4216 // Check for user-mode parameters and make sure that they are writeable
4218 if (PreviousMode
!= KernelMode
)
4220 ProbeForWritePointer(UBaseAddress
);
4221 ProbeForWriteUlong(URegionSize
);
4225 // Capture the current values
4227 PBaseAddress
= *UBaseAddress
;
4228 PRegionSize
= *URegionSize
;
4230 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4232 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4237 // Make sure the allocation isn't past the user area
4239 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4241 DPRINT1("Virtual free base above User Space\n");
4242 return STATUS_INVALID_PARAMETER_2
;
4246 // Make sure the allocation wouldn't overflow past the user area
4248 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4250 DPRINT1("Region size would overflow into kernel-memory\n");
4251 return STATUS_INVALID_PARAMETER_3
;
4255 // If this is for the current process, just use PsGetCurrentProcess
4257 if (ProcessHandle
== NtCurrentProcess())
4259 Process
= CurrentProcess
;
4264 // Otherwise, reference the process with VM rights and attach to it if
4265 // this isn't the current process. We must attach because we'll be touching
4266 // PTEs and PDEs that belong to user-mode memory, and also touching the
4267 // Working Set which is stored in Hyperspace.
4269 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4270 PROCESS_VM_OPERATION
,
4275 if (!NT_SUCCESS(Status
)) return Status
;
4276 if (CurrentProcess
!= Process
)
4278 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4284 // Lock the address space
4286 AddressSpace
= MmGetCurrentAddressSpace();
4287 MmLockAddressSpace(AddressSpace
);
4290 // If the address space is being deleted, fail the de-allocation since it's
4291 // too late to do anything about it
4293 if (Process
->VmDeleted
)
4295 DPRINT1("Process is dead\n");
4296 Status
= STATUS_PROCESS_IS_TERMINATING
;
4301 // Compute start and end addresses, and locate the VAD
4303 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4304 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4305 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4308 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4309 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4314 // If the range exceeds the VAD's ending VPN, fail this request
4316 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4318 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4319 Status
= STATUS_UNABLE_TO_FREE_VM
;
4324 // These ASSERTs are here because ReactOS ARM3 does not currently implement
4325 // any other kinds of VADs.
4327 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
== 1);
4328 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4329 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4332 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4333 // and that is is an ARM3 memory area, and not a section view, as we currently
4334 // don't support freeing those though this interface.
4336 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4338 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4341 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4343 if (FreeType
& MEM_RELEASE
)
4346 // Is the caller trying to remove the whole VAD, or remove only a portion
4347 // of it? If no region size is specified, then the assumption is that the
4348 // whole VAD is to be destroyed
4353 // The caller must specify the base address identically to the range
4354 // that is stored in the VAD.
4356 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4358 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4359 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4364 // Now compute the actual start/end addresses based on the VAD
4366 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4367 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4370 // Finally lock the working set and remove the VAD from the VAD tree
4372 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4373 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4374 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4379 // This means the caller wants to release a specific region within
4380 // the range. We have to find out which range this is -- the following
4381 // possibilities exist plus their union (CASE D):
4383 // STARTING ADDRESS ENDING ADDRESS
4384 // [<========][========================================][=========>]
4385 // CASE A CASE B CASE C
4388 // First, check for case A or D
4390 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4395 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4398 // This is the easiest one to handle -- it is identical to
4399 // the code path above when the caller sets a zero region size
4400 // and the whole VAD is destroyed
4402 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4403 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4404 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4409 // This case is pretty easy too -- we compute a bunch of
4410 // pages to decommit, and then push the VAD's starting address
4411 // a bit further down, then decrement the commit charge
4413 // NOT YET IMPLEMENTED IN ARM3.
4415 DPRINT1("Case A not handled\n");
4416 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4420 // After analyzing the VAD, set it to NULL so that we don't
4421 // free it in the exit path
4429 // This is case B or case C. First check for case C
4431 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4433 PMEMORY_AREA MemoryArea
;
4436 // This is pretty easy and similar to case A. We compute the
4437 // amount of pages to decommit, update the VAD's commit charge
4438 // and then change the ending address of the VAD to be a bit
4441 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4442 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4446 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4447 // For ReactOS: shrink the corresponding memory area
4448 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4449 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
4450 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
4451 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4452 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
4457 // This is case B and the hardest one. Because we are removing
4458 // a chunk of memory from the very middle of the VAD, we must
4459 // actually split the VAD into two new VADs and compute the
4460 // commit charges for each of them, and reinsert new charges.
4462 // NOT YET IMPLEMENTED IN ARM3.
4464 DPRINT1("Case B not handled\n");
4465 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4470 // After analyzing the VAD, set it to NULL so that we don't
4471 // free it in the exit path
4478 // Now we have a range of pages to dereference, so call the right API
4479 // to do that and then release the working set, since we're done messing
4480 // around with process pages.
4482 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4483 MiUnlockWorkingSet(CurrentThread
, AddressSpace
);
4484 Status
= STATUS_SUCCESS
;
4488 // Update the process counters
4490 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4491 Process
->CommitCharge
-= CommitReduction
;
4492 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4495 // Unlock the address space and free the VAD in failure cases. Next,
4496 // detach from the target process so we can write the region size and the
4497 // base address to the correct source process, and dereference the target
4500 MmUnlockAddressSpace(AddressSpace
);
4501 if (Vad
) ExFreePool(Vad
);
4502 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4503 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4506 // Use SEH to safely return the region size and the base address of the
4507 // deallocation. If we get an access violation, don't return a failure code
4508 // as the deallocation *has* happened. The caller will just have to figure
4509 // out another way to find out where it is (such as VirtualQuery).
4513 *URegionSize
= PRegionSize
;
4514 *UBaseAddress
= (PVOID
)StartingAddress
;
4516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4524 // This is the decommit path. You cannot decommit from the following VADs in
4525 // Windows, so fail the vall
4527 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4528 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4529 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4531 DPRINT1("Trying to decommit from invalid VAD\n");
4532 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4537 // If the caller did not specify a region size, first make sure that this
4538 // region is actually committed. If it is, then compute the ending address
4539 // based on the VAD.
4543 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4545 DPRINT1("Decomitting non-committed memory\n");
4546 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4549 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4553 // Decommit the PTEs for the range plus the actual backing pages for the
4554 // range, then reduce that amount from the commit charge in the VAD
4556 CommitReduction
= MiAddressToPte(EndingAddress
) -
4557 MiAddressToPte(StartingAddress
) +
4559 MiDecommitPages((PVOID
)StartingAddress
,
4560 MiAddressToPte(EndingAddress
),
4563 ASSERT(CommitReduction
>= 0);
4564 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4565 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4568 // We are done, go to the exit path without freeing the VAD as it remains
4569 // valid since we have not released the allocation.
4572 Status
= STATUS_SUCCESS
;
4576 // In the failure path, we detach and derefernece the target process, and
4577 // return whatever failure code was sent.
4580 MmUnlockAddressSpace(AddressSpace
);
4581 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4582 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);