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);
1900 DPRINT1("HACK: Not increasing UsedPageTableEntries count!\n");
1902 else if (PteContents
.u
.Hard
.Valid
== 1)
1904 /* Get the PFN entry */
1905 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
1907 /* We don't support this yet */
1908 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
1910 /* Check if the page should not be accessible at all */
1911 if ((NewAccessProtection
& PAGE_NOACCESS
) ||
1912 (NewAccessProtection
& PAGE_GUARD
))
1919 /* Write the protection mask and write it with a TLB flush */
1920 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
1921 MiFlushTbAndCapture(Vad
,
1930 /* We don't support these cases yet */
1931 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
1932 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
1934 /* The PTE is already demand-zero, just update the protection mask */
1935 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
1941 /* Unlock the working set and update quota charges if needed, then return */
1942 //MiUnlockProcessWorkingSet(Thread, Process);
1946 /* Unlock the address space */
1947 MmUnlockAddressSpace(AddressSpace
);
1949 /* Return parameters */
1950 *NumberOfBytesToProtect
= (SIZE_T
)((PUCHAR
)EndingAddress
- (PUCHAR
)StartingAddress
+ 1);
1951 *BaseAddress
= (PVOID
)StartingAddress
;
1958 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
1959 IN PEPROCESS TargetProcess
,
1962 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
1965 // Sanity checks. The latter is because we only use this function with the
1966 // PFN lock not held, so it may go away in the future.
1968 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1969 ASSERT(OldIrql
== MM_NOIRQL
);
1972 // Also get the PPE and PXE. This is okay not to #ifdef because they will
1973 // return the same address as the PDE on 2-level page table systems.
1975 // If everything is already valid, there is nothing to do.
1977 PointerPpe
= MiAddressToPte(PointerPde
);
1978 PointerPxe
= MiAddressToPde(PointerPde
);
1979 if ((PointerPxe
->u
.Hard
.Valid
) &&
1980 (PointerPpe
->u
.Hard
.Valid
) &&
1981 (PointerPde
->u
.Hard
.Valid
))
1987 // At least something is invalid, so begin by getting the PTE for the PDE itself
1988 // and then lookup each additional level. We must do it in this precise order
1989 // because the pagfault.c code (as well as in Windows) depends that the next
1990 // level up (higher) must be valid when faulting a lower level
1992 PointerPte
= MiPteToAddress(PointerPde
);
1996 // Make sure APCs continued to be disabled
1998 ASSERT(KeAreAllApcsDisabled() == TRUE
);
2001 // First, make the PXE valid if needed
2003 if (!PointerPxe
->u
.Hard
.Valid
)
2005 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
2006 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2012 if (!PointerPpe
->u
.Hard
.Valid
)
2014 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
2015 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2019 // And finally, make the PDE itself valid.
2021 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
2024 // This should've worked the first time so the loop is really just for
2025 // show -- ASSERT that we're actually NOT going to be looping.
2027 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
2028 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
2029 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
2030 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
2031 !(PointerPpe
->u
.Hard
.Valid
) ||
2032 !(PointerPde
->u
.Hard
.Valid
));
2037 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
2043 PFN_NUMBER PageFrameIndex
;
2047 // Acquire the PFN lock and loop all the PTEs in the list
2049 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2050 for (i
= 0; i
!= Count
; i
++)
2053 // The PTE must currently be valid
2055 TempPte
= *ValidPteList
[i
];
2056 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
2059 // Get the PFN entry for the page itself, and then for its page table
2061 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
2062 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
2063 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
2066 // Decrement the share count on the page table, and then on the page
2069 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
2070 MI_SET_PFN_DELETED(Pfn1
);
2071 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
2074 // Make the page decommitted
2076 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
2080 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2081 // and then release the PFN lock
2084 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2089 MiDecommitPages(IN PVOID StartingAddress
,
2090 IN PMMPTE EndingPte
,
2091 IN PEPROCESS Process
,
2094 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
2095 ULONG CommitReduction
= 0;
2096 PMMPTE ValidPteList
[256];
2100 PUSHORT UsedPageTableEntries
;
2101 PETHREAD CurrentThread
= PsGetCurrentThread();
2104 // Get the PTE and PTE for the address, and lock the working set
2105 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2106 // commited range ends so that we can do the right accounting.
2108 PointerPde
= MiAddressToPde(StartingAddress
);
2109 PointerPte
= MiAddressToPte(StartingAddress
);
2110 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
2111 MiLockWorkingSet(CurrentThread
, &Process
->Vm
);
2114 // Make the PDE valid, and now loop through each page's worth of data
2116 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2117 while (PointerPte
<= EndingPte
)
2120 // Check if we've crossed a PDE boundary
2122 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
2125 // Get the new PDE and flush the valid PTEs we had built up until
2126 // now. This helps reduce the amount of TLB flushing we have to do.
2127 // Note that Windows does a much better job using timestamps and
2128 // such, and does not flush the entire TLB all the time, but right
2129 // now we have bigger problems to worry about than TLB flushing.
2131 PointerPde
= MiAddressToPde(StartingAddress
);
2134 MiProcessValidPteList(ValidPteList
, PteCount
);
2139 // Make this PDE valid
2141 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2145 // Read this PTE. It might be active or still demand-zero.
2147 PteContents
= *PointerPte
;
2148 if (PteContents
.u
.Long
)
2151 // The PTE is active. It might be valid and in a working set, or
2152 // it might be a prototype PTE or paged out or even in transition.
2154 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
2157 // It's already decommited, so there's nothing for us to do here
2164 // Remove it from the counters, and check if it was valid or not
2166 //Process->NumberOfPrivatePages--;
2167 if (PteContents
.u
.Hard
.Valid
)
2170 // It's valid. At this point make sure that it is not a ROS
2171 // PFN. Also, we don't support ProtoPTEs in this code path.
2173 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
2174 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
2175 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
2178 // Flush any pending PTEs that we had not yet flushed, if our
2179 // list has gotten too big, then add this PTE to the flush list.
2181 if (PteCount
== 256)
2183 MiProcessValidPteList(ValidPteList
, PteCount
);
2186 ValidPteList
[PteCount
++] = PointerPte
;
2191 // We do not support any of these other scenarios at the moment
2193 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2194 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2195 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
2198 // So the only other possibility is that it is still a demand
2199 // zero PTE, in which case we undo the accounting we did
2200 // earlier and simply make the page decommitted.
2202 //Process->NumberOfPrivatePages++;
2203 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2210 // This used to be a zero PTE and it no longer is, so we must add a
2211 // reference to the pagetable.
2213 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(StartingAddress
)];
2214 (*UsedPageTableEntries
)++;
2215 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
2218 // Next, we account for decommitted PTEs and make the PTE as such
2220 if (PointerPte
> CommitPte
) CommitReduction
++;
2221 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
2225 // Move to the next PTE and the next address
2228 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2232 // Flush any dangling PTEs from the loop in the last page table, and then
2233 // release the working set and return the commit reduction accounting.
2235 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2236 MiUnlockWorkingSet(CurrentThread
, &Process
->Vm
);
2237 return CommitReduction
;
2240 /* PUBLIC FUNCTIONS ***********************************************************/
2247 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2258 MmSecureVirtualMemory(IN PVOID Address
,
2262 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2271 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2273 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2276 /* SYSTEM CALLS ***************************************************************/
2280 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2281 IN PVOID BaseAddress
,
2283 IN SIZE_T NumberOfBytesToRead
,
2284 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2286 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2288 NTSTATUS Status
= STATUS_SUCCESS
;
2289 SIZE_T BytesRead
= 0;
2293 // Check if we came from user mode
2295 if (PreviousMode
!= KernelMode
)
2298 // Validate the read addresses
2300 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2301 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2302 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2303 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2306 // Don't allow to write into kernel space
2308 return STATUS_ACCESS_VIOLATION
;
2312 // Enter SEH for probe
2317 // Probe the output value
2319 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2321 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2324 // Get exception code
2326 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2332 // Don't do zero-byte transfers
2334 if (NumberOfBytesToRead
)
2337 // Reference the process
2339 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2345 if (NT_SUCCESS(Status
))
2350 Status
= MmCopyVirtualMemory(Process
,
2352 PsGetCurrentProcess(),
2354 NumberOfBytesToRead
,
2359 // Dereference the process
2361 ObDereferenceObject(Process
);
2366 // Check if the caller sent this parameter
2368 if (NumberOfBytesRead
)
2371 // Enter SEH to guard write
2376 // Return the number of bytes read
2378 *NumberOfBytesRead
= BytesRead
;
2380 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2394 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2395 IN PVOID BaseAddress
,
2397 IN SIZE_T NumberOfBytesToWrite
,
2398 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2400 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2402 NTSTATUS Status
= STATUS_SUCCESS
;
2403 SIZE_T BytesWritten
= 0;
2407 // Check if we came from user mode
2409 if (PreviousMode
!= KernelMode
)
2412 // Validate the read addresses
2414 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2415 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2416 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2417 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2420 // Don't allow to write into kernel space
2422 return STATUS_ACCESS_VIOLATION
;
2426 // Enter SEH for probe
2431 // Probe the output value
2433 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2435 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2438 // Get exception code
2440 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2446 // Don't do zero-byte transfers
2448 if (NumberOfBytesToWrite
)
2451 // Reference the process
2453 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2459 if (NT_SUCCESS(Status
))
2464 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2468 NumberOfBytesToWrite
,
2473 // Dereference the process
2475 ObDereferenceObject(Process
);
2480 // Check if the caller sent this parameter
2482 if (NumberOfBytesWritten
)
2485 // Enter SEH to guard write
2490 // Return the number of bytes written
2492 *NumberOfBytesWritten
= BytesWritten
;
2494 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2508 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2509 IN OUT PVOID
*UnsafeBaseAddress
,
2510 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2511 IN ULONG NewAccessProtection
,
2512 OUT PULONG UnsafeOldAccessProtection
)
2515 ULONG OldAccessProtection
;
2517 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2518 PVOID BaseAddress
= NULL
;
2519 SIZE_T NumberOfBytesToProtect
= 0;
2520 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2522 BOOLEAN Attached
= FALSE
;
2523 KAPC_STATE ApcState
;
2527 // Check for valid protection flags
2529 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2530 if (Protection
!= PAGE_NOACCESS
&&
2531 Protection
!= PAGE_READONLY
&&
2532 Protection
!= PAGE_READWRITE
&&
2533 Protection
!= PAGE_WRITECOPY
&&
2534 Protection
!= PAGE_EXECUTE
&&
2535 Protection
!= PAGE_EXECUTE_READ
&&
2536 Protection
!= PAGE_EXECUTE_READWRITE
&&
2537 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2542 return STATUS_INVALID_PAGE_PROTECTION
;
2546 // Check if we came from user mode
2548 if (PreviousMode
!= KernelMode
)
2551 // Enter SEH for probing
2556 // Validate all outputs
2558 ProbeForWritePointer(UnsafeBaseAddress
);
2559 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2560 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2565 BaseAddress
= *UnsafeBaseAddress
;
2566 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2568 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2571 // Get exception code
2573 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2582 BaseAddress
= *UnsafeBaseAddress
;
2583 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2587 // Catch illegal base address
2589 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2592 // Catch illegal region size
2594 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2599 return STATUS_INVALID_PARAMETER_3
;
2603 // 0 is also illegal
2605 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2608 // Get a reference to the process
2610 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2611 PROCESS_VM_OPERATION
,
2616 if (!NT_SUCCESS(Status
)) return Status
;
2619 // Check if we should attach
2621 if (CurrentProcess
!= Process
)
2626 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2631 // Do the actual work
2633 Status
= MiProtectVirtualMemory(Process
,
2635 &NumberOfBytesToProtect
,
2636 NewAccessProtection
,
2637 &OldAccessProtection
);
2642 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2645 // Release reference
2647 ObDereferenceObject(Process
);
2650 // Enter SEH to return data
2655 // Return data to user
2657 *UnsafeOldAccessProtection
= OldAccessProtection
;
2658 *UnsafeBaseAddress
= BaseAddress
;
2659 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2674 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2675 IN OUT PVOID
*BaseAddress
,
2676 IN OUT PSIZE_T NumberOfBytesToLock
,
2680 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2682 BOOLEAN Attached
= FALSE
;
2683 KAPC_STATE ApcState
;
2684 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2685 PVOID CapturedBaseAddress
;
2686 SIZE_T CapturedBytesToLock
;
2692 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2695 // Invalid set of flags
2697 return STATUS_INVALID_PARAMETER
;
2701 // At least one flag must be specified
2703 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2708 return STATUS_INVALID_PARAMETER
;
2712 // Enter SEH for probing
2717 // Validate output data
2719 ProbeForWritePointer(BaseAddress
);
2720 ProbeForWriteSize_t(NumberOfBytesToLock
);
2725 CapturedBaseAddress
= *BaseAddress
;
2726 CapturedBytesToLock
= *NumberOfBytesToLock
;
2728 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2731 // Get exception code
2733 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2738 // Catch illegal base address
2740 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2743 // Catch illegal region size
2745 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2750 return STATUS_INVALID_PARAMETER
;
2754 // 0 is also illegal
2756 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2759 // Get a reference to the process
2761 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2762 PROCESS_VM_OPERATION
,
2767 if (!NT_SUCCESS(Status
)) return Status
;
2770 // Check if this is is system-mapped
2772 if (MapType
& MAP_SYSTEM
)
2775 // Check for required privilege
2777 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2780 // Fail: Don't have it
2782 ObDereferenceObject(Process
);
2783 return STATUS_PRIVILEGE_NOT_HELD
;
2788 // Check if we should attach
2790 if (CurrentProcess
!= Process
)
2795 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2807 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2810 // Release reference
2812 ObDereferenceObject(Process
);
2815 // Enter SEH to return data
2820 // Return data to user
2822 *BaseAddress
= CapturedBaseAddress
;
2823 *NumberOfBytesToLock
= 0;
2825 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2828 // Get exception code
2830 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2837 return STATUS_SUCCESS
;
2842 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
2843 IN OUT PVOID
*BaseAddress
,
2844 IN OUT PSIZE_T NumberOfBytesToUnlock
,
2848 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2850 BOOLEAN Attached
= FALSE
;
2851 KAPC_STATE ApcState
;
2852 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2853 PVOID CapturedBaseAddress
;
2854 SIZE_T CapturedBytesToUnlock
;
2860 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2863 // Invalid set of flags
2865 return STATUS_INVALID_PARAMETER
;
2869 // At least one flag must be specified
2871 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2876 return STATUS_INVALID_PARAMETER
;
2880 // Enter SEH for probing
2885 // Validate output data
2887 ProbeForWritePointer(BaseAddress
);
2888 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
2893 CapturedBaseAddress
= *BaseAddress
;
2894 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
2896 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2899 // Get exception code
2901 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2906 // Catch illegal base address
2908 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2911 // Catch illegal region size
2913 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
2918 return STATUS_INVALID_PARAMETER
;
2922 // 0 is also illegal
2924 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
2927 // Get a reference to the process
2929 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2930 PROCESS_VM_OPERATION
,
2935 if (!NT_SUCCESS(Status
)) return Status
;
2938 // Check if this is is system-mapped
2940 if (MapType
& MAP_SYSTEM
)
2943 // Check for required privilege
2945 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2948 // Fail: Don't have it
2950 ObDereferenceObject(Process
);
2951 return STATUS_PRIVILEGE_NOT_HELD
;
2956 // Check if we should attach
2958 if (CurrentProcess
!= Process
)
2963 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2975 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2978 // Release reference
2980 ObDereferenceObject(Process
);
2983 // Enter SEH to return data
2988 // Return data to user
2990 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2991 *NumberOfBytesToUnlock
= 0;
2993 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2996 // Get exception code
2998 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3005 return STATUS_SUCCESS
;
3010 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
3011 IN OUT PVOID
*BaseAddress
,
3012 IN OUT PSIZE_T NumberOfBytesToFlush
,
3013 OUT PIO_STATUS_BLOCK IoStatusBlock
)
3017 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3018 PVOID CapturedBaseAddress
;
3019 SIZE_T CapturedBytesToFlush
;
3020 IO_STATUS_BLOCK LocalStatusBlock
;
3024 // Check if we came from user mode
3026 if (PreviousMode
!= KernelMode
)
3029 // Enter SEH for probing
3034 // Validate all outputs
3036 ProbeForWritePointer(BaseAddress
);
3037 ProbeForWriteSize_t(NumberOfBytesToFlush
);
3038 ProbeForWriteIoStatusBlock(IoStatusBlock
);
3043 CapturedBaseAddress
= *BaseAddress
;
3044 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3046 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3049 // Get exception code
3051 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3060 CapturedBaseAddress
= *BaseAddress
;
3061 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
3065 // Catch illegal base address
3067 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3070 // Catch illegal region size
3072 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
3077 return STATUS_INVALID_PARAMETER
;
3081 // Get a reference to the process
3083 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3084 PROCESS_VM_OPERATION
,
3089 if (!NT_SUCCESS(Status
)) return Status
;
3094 Status
= MmFlushVirtualMemory(Process
,
3095 &CapturedBaseAddress
,
3096 &CapturedBytesToFlush
,
3100 // Release reference
3102 ObDereferenceObject(Process
);
3105 // Enter SEH to return data
3110 // Return data to user
3112 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
3113 *NumberOfBytesToFlush
= 0;
3114 *IoStatusBlock
= LocalStatusBlock
;
3116 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3132 NtGetWriteWatch(IN HANDLE ProcessHandle
,
3134 IN PVOID BaseAddress
,
3135 IN SIZE_T RegionSize
,
3136 IN PVOID
*UserAddressArray
,
3137 OUT PULONG_PTR EntriesInUserAddressArray
,
3138 OUT PULONG Granularity
)
3143 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3144 ULONG_PTR CapturedEntryCount
;
3148 // Check if we came from user mode
3150 if (PreviousMode
!= KernelMode
)
3153 // Enter SEH for probing
3158 // Catch illegal base address
3160 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3163 // Catch illegal region size
3165 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3170 return STATUS_INVALID_PARAMETER_3
;
3174 // Validate all data
3176 ProbeForWriteSize_t(EntriesInUserAddressArray
);
3177 ProbeForWriteUlong(Granularity
);
3182 CapturedEntryCount
= *EntriesInUserAddressArray
;
3185 // Must have a count
3187 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
3190 // Can't be larger than the maximum
3192 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
3197 return STATUS_INVALID_PARAMETER_5
;
3201 // Probe the actual array
3203 ProbeForWrite(UserAddressArray
,
3204 CapturedEntryCount
* sizeof(PVOID
),
3207 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3210 // Get exception code
3212 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3221 CapturedEntryCount
= *EntriesInUserAddressArray
;
3222 ASSERT(CapturedEntryCount
!= 0);
3226 // Check if this is a local request
3228 if (ProcessHandle
== NtCurrentProcess())
3231 // No need to reference the process
3233 Process
= PsGetCurrentProcess();
3238 // Reference the target
3240 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3241 PROCESS_VM_OPERATION
,
3246 if (!NT_SUCCESS(Status
)) return Status
;
3250 // Compute the last address and validate it
3252 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3253 if (BaseAddress
> EndAddress
)
3258 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3259 return STATUS_INVALID_PARAMETER_4
;
3268 // Dereference if needed
3270 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3273 // Enter SEH to return data
3278 // Return data to user
3280 *EntriesInUserAddressArray
= 0;
3281 *Granularity
= PAGE_SIZE
;
3283 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3286 // Get exception code
3288 Status
= _SEH2_GetExceptionCode();
3295 return STATUS_SUCCESS
;
3303 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3304 IN PVOID BaseAddress
,
3305 IN SIZE_T RegionSize
)
3310 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3311 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3314 // Catch illegal base address
3316 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3319 // Catch illegal region size
3321 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3326 return STATUS_INVALID_PARAMETER_3
;
3330 // Check if this is a local request
3332 if (ProcessHandle
== NtCurrentProcess())
3335 // No need to reference the process
3337 Process
= PsGetCurrentProcess();
3342 // Reference the target
3344 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3345 PROCESS_VM_OPERATION
,
3350 if (!NT_SUCCESS(Status
)) return Status
;
3354 // Compute the last address and validate it
3356 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3357 if (BaseAddress
> EndAddress
)
3362 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3363 return STATUS_INVALID_PARAMETER_3
;
3372 // Dereference if needed
3374 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3379 return STATUS_SUCCESS
;
3384 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3385 IN PVOID BaseAddress
,
3386 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3387 OUT PVOID MemoryInformation
,
3388 IN SIZE_T MemoryInformationLength
,
3389 OUT PSIZE_T ReturnLength
)
3391 NTSTATUS Status
= STATUS_SUCCESS
;
3392 KPROCESSOR_MODE PreviousMode
;
3394 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3396 /* Bail out if the address is invalid */
3397 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3399 /* Probe return buffer */
3400 PreviousMode
= ExGetPreviousMode();
3401 if (PreviousMode
!= KernelMode
)
3405 ProbeForWrite(MemoryInformation
,
3406 MemoryInformationLength
,
3409 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3411 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3413 Status
= _SEH2_GetExceptionCode();
3417 if (!NT_SUCCESS(Status
))
3423 switch(MemoryInformationClass
)
3425 case MemoryBasicInformation
:
3426 /* Validate the size information of the class */
3427 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3429 /* The size is invalid */
3430 return STATUS_INFO_LENGTH_MISMATCH
;
3432 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3435 MemoryInformationLength
,
3439 case MemorySectionName
:
3440 /* Validate the size information of the class */
3441 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3443 /* The size is invalid */
3444 return STATUS_INFO_LENGTH_MISMATCH
;
3446 Status
= MiQueryMemorySectionName(ProcessHandle
,
3449 MemoryInformationLength
,
3452 case MemoryWorkingSetList
:
3453 case MemoryBasicVlmInformation
:
3455 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3467 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3468 IN OUT PVOID
* UBaseAddress
,
3469 IN ULONG_PTR ZeroBits
,
3470 IN OUT PSIZE_T URegionSize
,
3471 IN ULONG AllocationType
,
3475 PMEMORY_AREA MemoryArea
;
3476 PFN_NUMBER PageCount
;
3477 PMMVAD Vad
, FoundVad
;
3478 PUSHORT UsedPageTableEntries
;
3480 PMMSUPPORT AddressSpace
;
3482 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3483 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3484 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3485 PETHREAD CurrentThread
= PsGetCurrentThread();
3486 KAPC_STATE ApcState
;
3487 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3488 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3490 PMMPTE PointerPte
, PointerPde
, LastPte
;
3493 /* Check for valid Zero bits */
3496 DPRINT1("Too many zero bits\n");
3497 return STATUS_INVALID_PARAMETER_3
;
3500 /* Check for valid Allocation Types */
3501 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3502 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3504 DPRINT1("Invalid Allocation Type\n");
3505 return STATUS_INVALID_PARAMETER_5
;
3508 /* Check for at least one of these Allocation Types to be set */
3509 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3511 DPRINT1("No memory allocation base type\n");
3512 return STATUS_INVALID_PARAMETER_5
;
3515 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3516 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3518 DPRINT1("Invalid use of MEM_RESET\n");
3519 return STATUS_INVALID_PARAMETER_5
;
3522 /* Check if large pages are being used */
3523 if (AllocationType
& MEM_LARGE_PAGES
)
3525 /* Large page allocations MUST be committed */
3526 if (!(AllocationType
& MEM_COMMIT
))
3528 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3529 return STATUS_INVALID_PARAMETER_5
;
3532 /* These flags are not allowed with large page allocations */
3533 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3535 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3536 return STATUS_INVALID_PARAMETER_5
;
3540 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3541 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3543 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3544 return STATUS_INVALID_PARAMETER_5
;
3547 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3548 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3550 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3551 return STATUS_INVALID_PARAMETER_5
;
3554 /* Check for valid MEM_PHYSICAL usage */
3555 if (AllocationType
& MEM_PHYSICAL
)
3557 /* Only these flags are allowed with MEM_PHYSIAL */
3558 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3560 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3561 return STATUS_INVALID_PARAMETER_5
;
3564 /* Then make sure PAGE_READWRITE is used */
3565 if (Protect
!= PAGE_READWRITE
)
3567 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3568 return STATUS_INVALID_PARAMETER_6
;
3573 // Force PAGE_READWRITE for everything, for now
3575 Protect
= PAGE_READWRITE
;
3577 /* Calculate the protection mask and make sure it's valid */
3578 ProtectionMask
= MiMakeProtectionMask(Protect
);
3579 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3581 DPRINT1("Invalid protection mask\n");
3582 return STATUS_INVALID_PAGE_PROTECTION
;
3588 /* Check for user-mode parameters */
3589 if (PreviousMode
!= KernelMode
)
3591 /* Make sure they are writable */
3592 ProbeForWritePointer(UBaseAddress
);
3593 ProbeForWriteUlong(URegionSize
);
3596 /* Capture their values */
3597 PBaseAddress
= *UBaseAddress
;
3598 PRegionSize
= *URegionSize
;
3600 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3602 /* Return the exception code */
3603 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3607 /* Make sure the allocation isn't past the VAD area */
3608 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3610 DPRINT1("Virtual allocation base above User Space\n");
3611 return STATUS_INVALID_PARAMETER_2
;
3614 /* Make sure the allocation wouldn't overflow past the VAD area */
3615 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3617 DPRINT1("Region size would overflow into kernel-memory\n");
3618 return STATUS_INVALID_PARAMETER_4
;
3621 /* Make sure there's a size specified */
3624 DPRINT1("Region size is invalid (zero)\n");
3625 return STATUS_INVALID_PARAMETER_4
;
3629 // If this is for the current process, just use PsGetCurrentProcess
3631 if (ProcessHandle
== NtCurrentProcess())
3633 Process
= CurrentProcess
;
3638 // Otherwise, reference the process with VM rights and attach to it if
3639 // this isn't the current process. We must attach because we'll be touching
3640 // PTEs and PDEs that belong to user-mode memory, and also touching the
3641 // Working Set which is stored in Hyperspace.
3643 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3644 PROCESS_VM_OPERATION
,
3649 if (!NT_SUCCESS(Status
)) return Status
;
3650 if (CurrentProcess
!= Process
)
3652 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3658 // Check for large page allocations and make sure that the required privilege
3659 // is being held, before attempting to handle them.
3661 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3662 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3664 /* Fail without it */
3665 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3666 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3667 goto FailPathNoLock
;
3671 // Assert on the things we don't yet support
3673 ASSERT(ZeroBits
== 0);
3674 ASSERT((AllocationType
& MEM_LARGE_PAGES
) == 0);
3675 ASSERT((AllocationType
& MEM_PHYSICAL
) == 0);
3676 ASSERT((AllocationType
& MEM_WRITE_WATCH
) == 0);
3677 ASSERT((AllocationType
& MEM_TOP_DOWN
) == 0);
3678 ASSERT((AllocationType
& MEM_RESET
) == 0);
3679 ASSERT(Process
->VmTopDown
== 0);
3682 // Check if the caller is reserving memory, or committing memory and letting
3683 // us pick the base address
3685 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3688 // Do not allow COPY_ON_WRITE through this API
3690 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3692 DPRINT1("Copy on write not allowed through this path\n");
3693 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3694 goto FailPathNoLock
;
3698 // Does the caller have an address in mind, or is this a blind commit?
3703 // This is a blind commit, all we need is the region size
3705 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3706 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3708 StartingAddress
= 0;
3713 // This is a reservation, so compute the starting address on the
3714 // expected 64KB granularity, and see where the ending address will
3715 // fall based on the aligned address and the passed in region size
3717 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3718 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3719 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3723 // Allocate and initialize the VAD
3725 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3726 ASSERT(Vad
!= NULL
);
3727 Vad
->u
.LongFlags
= 0;
3728 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3729 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3730 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3731 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3734 // Lock the address space and make sure the process isn't already dead
3736 AddressSpace
= MmGetCurrentAddressSpace();
3737 MmLockAddressSpace(AddressSpace
);
3738 if (Process
->VmDeleted
)
3740 Status
= STATUS_PROCESS_IS_TERMINATING
;
3745 // Did we have a base address? If no, find a valid address that is 64KB
3746 // aligned in the VAD tree. Otherwise, make sure that the address range
3747 // which was passed in isn't already conflicting with an existing address
3752 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3755 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3757 if (!NT_SUCCESS(Status
)) goto FailPath
;
3760 // Now we know where the allocation ends. Make sure it doesn't end up
3761 // somewhere in kernel mode.
3763 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3764 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3766 Status
= STATUS_NO_MEMORY
;
3770 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3771 EndingAddress
>> PAGE_SHIFT
,
3775 // The address specified is in conflict!
3777 Status
= STATUS_CONFLICTING_ADDRESSES
;
3782 // Write out the VAD fields for this allocation
3784 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3785 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3788 // FIXME: Should setup VAD bitmap
3790 Status
= STATUS_SUCCESS
;
3793 // Lock the working set and insert the VAD into the process VAD tree
3795 MiLockProcessWorkingSet(Process
, CurrentThread
);
3796 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3797 MiInsertVad(Vad
, Process
);
3798 MiUnlockProcessWorkingSet(Process
, CurrentThread
);
3801 // Update the virtual size of the process, and if this is now the highest
3802 // virtual size we have ever seen, update the peak virtual size to reflect
3805 Process
->VirtualSize
+= PRegionSize
;
3806 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
3808 Process
->PeakVirtualSize
= Process
->VirtualSize
;
3812 // Release address space and detach and dereference the target process if
3813 // it was different from the current process
3815 MmUnlockAddressSpace(AddressSpace
);
3816 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3817 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3820 // Use SEH to write back the base address and the region size. In the case
3821 // of an exception, we do not return back the exception code, as the memory
3822 // *has* been allocated. The caller would now have to call VirtualQuery
3823 // or do some other similar trick to actually find out where its memory
3824 // allocation ended up
3828 *URegionSize
= PRegionSize
;
3829 *UBaseAddress
= (PVOID
)StartingAddress
;
3831 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3835 return STATUS_SUCCESS
;
3839 // This is a MEM_COMMIT on top of an existing address which must have been
3840 // MEM_RESERVED already. Compute the start and ending base addresses based
3841 // on the user input, and then compute the actual region size once all the
3842 // alignments have been done.
3844 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
3845 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
3846 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
3849 // Lock the address space and make sure the process isn't already dead
3851 AddressSpace
= MmGetCurrentAddressSpace();
3852 MmLockAddressSpace(AddressSpace
);
3853 if (Process
->VmDeleted
)
3855 DPRINT1("Process is dying\n");
3856 Status
= STATUS_PROCESS_IS_TERMINATING
;
3861 // Get the VAD for this address range, and make sure it exists
3863 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3864 EndingAddress
>> PAGE_SHIFT
,
3868 DPRINT1("Could not find a VAD for this allocation\n");
3869 Status
= STATUS_CONFLICTING_ADDRESSES
;
3874 // These kinds of VADs are illegal for this Windows function when trying to
3875 // commit an existing range
3877 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
3878 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
3879 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
3881 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
3882 Status
= STATUS_CONFLICTING_ADDRESSES
;
3887 // Make sure that this address range actually fits within the VAD for it
3889 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
3890 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
3892 DPRINT1("Address range does not fit into the VAD\n");
3893 Status
= STATUS_CONFLICTING_ADDRESSES
;
3898 // If this is an existing section view, we call the old RosMm routine which
3899 // has the relevant code required to handle the section scenario. In the future
3900 // we will limit this even more so that there's almost nothing that the code
3901 // needs to do, and it will become part of section.c in RosMm
3903 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
3904 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
3906 return MiRosAllocateVirtualMemory(ProcessHandle
,
3917 // Is this a previously reserved section being committed? If so, enter the
3918 // special section path
3920 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
3923 // You cannot commit large page sections through this API
3925 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
3927 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
3928 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3933 // You can only use caching flags on a rotate VAD
3935 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
3936 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
3938 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
3939 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3944 // We should make sure that the section's permissions aren't being messed with
3946 if (FoundVad
->u
.VadFlags
.NoChange
)
3948 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
3952 // ARM3 does not support file-backed sections, only shared memory
3954 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
3957 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
3959 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
3960 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
3962 DPRINT1("Invalid page protection for rotate VAD\n");
3963 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3968 // Compute PTE addresses and the quota charge, then grab the commit lock
3970 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
3971 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
3972 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
3973 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
3976 // Get the segment template PTE and start looping each page
3978 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
3979 ASSERT(TempPte
.u
.Long
!= 0);
3980 while (PointerPte
<= LastPte
)
3983 // For each non-already-committed page, write the invalid template PTE
3985 if (PointerPte
->u
.Long
== 0)
3987 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3997 // Now do the commit accounting and release the lock
3999 ASSERT(QuotaCharge
>= QuotaFree
);
4000 QuotaCharge
-= QuotaFree
;
4001 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
4002 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
4005 // We are done with committing the section pages
4007 Status
= STATUS_SUCCESS
;
4012 // This is a specific ReactOS check because we only use normal VADs
4014 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
4017 // While this is an actual Windows check
4019 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
4022 // Throw out attempts to use copy-on-write through this API path
4024 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
4026 DPRINT1("Write copy attempted when not allowed\n");
4027 Status
= STATUS_INVALID_PAGE_PROTECTION
;
4032 // Initialize a demand-zero PTE
4035 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
4038 // Get the PTE, PDE and the last PTE for this address range
4040 PointerPde
= MiAddressToPde(StartingAddress
);
4041 PointerPte
= MiAddressToPte(StartingAddress
);
4042 LastPte
= MiAddressToPte(EndingAddress
);
4045 // Update the commit charge in the VAD as well as in the process, and check
4046 // if this commit charge was now higher than the last recorded peak, in which
4047 // case we also update the peak
4049 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
4050 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
4051 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
4053 Process
->CommitChargePeak
= Process
->CommitCharge
;
4057 // Lock the working set while we play with user pages and page tables
4059 //MiLockWorkingSet(CurrentThread, AddressSpace);
4062 // Make the current page table valid, and then loop each page within it
4064 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4065 while (PointerPte
<= LastPte
)
4068 // Have we crossed into a new page table?
4070 if (!(((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)))
4073 // Get the PDE and now make it valid too
4075 PointerPde
= MiAddressToPte(PointerPte
);
4076 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
4080 // Is this a zero PTE as expected?
4082 if (PointerPte
->u
.Long
== 0)
4085 // First increment the count of pages in the page table for this
4088 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
4089 (*UsedPageTableEntries
)++;
4090 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
4093 // And now write the invalid demand-zero PTE as requested
4095 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4097 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
4100 // If the PTE was already decommitted, there is nothing else to do
4101 // but to write the new demand-zero PTE
4103 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
4105 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
4108 // We don't handle these scenarios yet
4110 if (PointerPte
->u
.Soft
.Valid
== 0)
4112 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
4113 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
4117 // There's a change in protection, remember this for later, but do
4118 // not yet handle it.
4120 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect
);
4121 ChangeProtection
= TRUE
;
4125 // Move to the next PTE
4131 // This path is not yet handled
4133 ASSERT(ChangeProtection
== FALSE
);
4136 // Release the working set lock, unlock the address space, and detach from
4137 // the target process if it was not the current process. Also dereference the
4138 // target process if this wasn't the case.
4140 //MiUnlockProcessWorkingSet(Process, CurrentThread);
4141 Status
= STATUS_SUCCESS
;
4143 MmUnlockAddressSpace(AddressSpace
);
4145 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4146 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4149 // Use SEH to write back the base address and the region size. In the case
4150 // of an exception, we strangely do return back the exception code, even
4151 // though the memory *has* been allocated. This mimics Windows behavior and
4152 // there is not much we can do about it.
4156 *URegionSize
= PRegionSize
;
4157 *UBaseAddress
= (PVOID
)StartingAddress
;
4159 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4161 Status
= _SEH2_GetExceptionCode();
4172 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
4173 IN PVOID
* UBaseAddress
,
4174 IN PSIZE_T URegionSize
,
4177 PMEMORY_AREA MemoryArea
;
4180 ULONG_PTR CommitReduction
= 0;
4181 ULONG_PTR StartingAddress
, EndingAddress
;
4185 PMMSUPPORT AddressSpace
;
4186 PETHREAD CurrentThread
= PsGetCurrentThread();
4187 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
4188 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
4189 KAPC_STATE ApcState
;
4190 BOOLEAN Attached
= FALSE
;
4194 // Only two flags are supported
4196 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
4198 DPRINT1("Invalid FreeType\n");
4199 return STATUS_INVALID_PARAMETER_4
;
4203 // Check if no flag was used, or if both flags were used
4205 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
4206 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
4208 DPRINT1("Invalid FreeType combination\n");
4209 return STATUS_INVALID_PARAMETER_4
;
4213 // Enter SEH for probe and capture. On failure, return back to the caller
4214 // with an exception violation.
4219 // Check for user-mode parameters and make sure that they are writeable
4221 if (PreviousMode
!= KernelMode
)
4223 ProbeForWritePointer(UBaseAddress
);
4224 ProbeForWriteUlong(URegionSize
);
4228 // Capture the current values
4230 PBaseAddress
= *UBaseAddress
;
4231 PRegionSize
= *URegionSize
;
4233 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4235 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4240 // Make sure the allocation isn't past the user area
4242 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4244 DPRINT1("Virtual free base above User Space\n");
4245 return STATUS_INVALID_PARAMETER_2
;
4249 // Make sure the allocation wouldn't overflow past the user area
4251 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4253 DPRINT1("Region size would overflow into kernel-memory\n");
4254 return STATUS_INVALID_PARAMETER_3
;
4258 // If this is for the current process, just use PsGetCurrentProcess
4260 if (ProcessHandle
== NtCurrentProcess())
4262 Process
= CurrentProcess
;
4267 // Otherwise, reference the process with VM rights and attach to it if
4268 // this isn't the current process. We must attach because we'll be touching
4269 // PTEs and PDEs that belong to user-mode memory, and also touching the
4270 // Working Set which is stored in Hyperspace.
4272 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4273 PROCESS_VM_OPERATION
,
4278 if (!NT_SUCCESS(Status
)) return Status
;
4279 if (CurrentProcess
!= Process
)
4281 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4287 // Lock the address space
4289 AddressSpace
= MmGetCurrentAddressSpace();
4290 MmLockAddressSpace(AddressSpace
);
4293 // If the address space is being deleted, fail the de-allocation since it's
4294 // too late to do anything about it
4296 if (Process
->VmDeleted
)
4298 DPRINT1("Process is dead\n");
4299 Status
= STATUS_PROCESS_IS_TERMINATING
;
4304 // Compute start and end addresses, and locate the VAD
4306 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4307 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4308 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4311 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4312 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4317 // If the range exceeds the VAD's ending VPN, fail this request
4319 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4321 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4322 Status
= STATUS_UNABLE_TO_FREE_VM
;
4327 // These ASSERTs are here because ReactOS ARM3 does not currently implement
4328 // any other kinds of VADs.
4330 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
== 1);
4331 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4332 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4335 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4336 // and that is is an ARM3 memory area, and not a section view, as we currently
4337 // don't support freeing those though this interface.
4339 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4341 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4344 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4346 if (FreeType
& MEM_RELEASE
)
4349 // Is the caller trying to remove the whole VAD, or remove only a portion
4350 // of it? If no region size is specified, then the assumption is that the
4351 // whole VAD is to be destroyed
4356 // The caller must specify the base address identically to the range
4357 // that is stored in the VAD.
4359 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4361 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4362 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4367 // Now compute the actual start/end addresses based on the VAD
4369 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4370 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4373 // Finally lock the working set and remove the VAD from the VAD tree
4375 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4376 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4377 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4382 // This means the caller wants to release a specific region within
4383 // the range. We have to find out which range this is -- the following
4384 // possibilities exist plus their union (CASE D):
4386 // STARTING ADDRESS ENDING ADDRESS
4387 // [<========][========================================][=========>]
4388 // CASE A CASE B CASE C
4391 // First, check for case A or D
4393 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4398 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4401 // This is the easiest one to handle -- it is identical to
4402 // the code path above when the caller sets a zero region size
4403 // and the whole VAD is destroyed
4405 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4406 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4407 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4412 // This case is pretty easy too -- we compute a bunch of
4413 // pages to decommit, and then push the VAD's starting address
4414 // a bit further down, then decrement the commit charge
4416 // NOT YET IMPLEMENTED IN ARM3.
4418 DPRINT1("Case A not handled\n");
4419 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4423 // After analyzing the VAD, set it to NULL so that we don't
4424 // free it in the exit path
4432 // This is case B or case C. First check for case C
4434 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4436 PMEMORY_AREA MemoryArea
;
4439 // This is pretty easy and similar to case A. We compute the
4440 // amount of pages to decommit, update the VAD's commit charge
4441 // and then change the ending address of the VAD to be a bit
4444 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4445 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4449 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4450 // For ReactOS: shrink the corresponding memory area
4451 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4452 ASSERT(Vad
->StartingVpn
<< PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->StartingAddress
);
4453 ASSERT((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
== (ULONG_PTR
)MemoryArea
->EndingAddress
);
4454 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4455 MemoryArea
->EndingAddress
= (PVOID
)(((Vad
->EndingVpn
+ 1) << PAGE_SHIFT
) - 1);
4460 // This is case B and the hardest one. Because we are removing
4461 // a chunk of memory from the very middle of the VAD, we must
4462 // actually split the VAD into two new VADs and compute the
4463 // commit charges for each of them, and reinsert new charges.
4465 // NOT YET IMPLEMENTED IN ARM3.
4467 DPRINT1("Case B not handled\n");
4468 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4473 // After analyzing the VAD, set it to NULL so that we don't
4474 // free it in the exit path
4481 // Now we have a range of pages to dereference, so call the right API
4482 // to do that and then release the working set, since we're done messing
4483 // around with process pages.
4485 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4486 MiUnlockWorkingSet(CurrentThread
, AddressSpace
);
4487 Status
= STATUS_SUCCESS
;
4491 // Update the process counters
4493 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4494 Process
->CommitCharge
-= CommitReduction
;
4495 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4498 // Unlock the address space and free the VAD in failure cases. Next,
4499 // detach from the target process so we can write the region size and the
4500 // base address to the correct source process, and dereference the target
4503 MmUnlockAddressSpace(AddressSpace
);
4504 if (Vad
) ExFreePool(Vad
);
4505 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4506 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4509 // Use SEH to safely return the region size and the base address of the
4510 // deallocation. If we get an access violation, don't return a failure code
4511 // as the deallocation *has* happened. The caller will just have to figure
4512 // out another way to find out where it is (such as VirtualQuery).
4516 *URegionSize
= PRegionSize
;
4517 *UBaseAddress
= (PVOID
)StartingAddress
;
4519 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4527 // This is the decommit path. You cannot decommit from the following VADs in
4528 // Windows, so fail the vall
4530 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4531 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4532 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4534 DPRINT1("Trying to decommit from invalid VAD\n");
4535 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4540 // If the caller did not specify a region size, first make sure that this
4541 // region is actually committed. If it is, then compute the ending address
4542 // based on the VAD.
4546 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4548 DPRINT1("Decomitting non-committed memory\n");
4549 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4552 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4556 // Decommit the PTEs for the range plus the actual backing pages for the
4557 // range, then reduce that amount from the commit charge in the VAD
4559 CommitReduction
= MiAddressToPte(EndingAddress
) -
4560 MiAddressToPte(StartingAddress
) +
4562 MiDecommitPages((PVOID
)StartingAddress
,
4563 MiAddressToPte(EndingAddress
),
4566 ASSERT(CommitReduction
>= 0);
4567 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4568 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4571 // We are done, go to the exit path without freeing the VAD as it remains
4572 // valid since we have not released the allocation.
4575 Status
= STATUS_SUCCESS
;
4579 // In the failure path, we detach and derefernece the target process, and
4580 // return whatever failure code was sent.
4583 MmUnlockAddressSpace(AddressSpace
);
4584 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4585 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);