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
);
29 /* PRIVATE FUNCTIONS **********************************************************/
33 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress
,
34 IN ULONG_PTR EndingAddress
,
38 PMMPTE PointerPte
, LastPte
, PointerPde
;
41 /* Compute starting and ending PTE and PDE addresses */
42 PointerPde
= MiAddressToPde(StartingAddress
);
43 PointerPte
= MiAddressToPte(StartingAddress
);
44 LastPte
= MiAddressToPte(EndingAddress
);
46 /* Handle commited pages first */
47 if (Vad
->u
.VadFlags
.MemCommit
== 1)
49 /* This is a committed VAD, so Assume the whole range is committed */
50 CommittedPages
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
52 /* Is the PDE demand-zero? */
53 PointerPde
= MiAddressToPte(PointerPte
);
54 if (PointerPde
->u
.Long
!= 0)
56 /* It is not. Is it valid? */
57 if (PointerPde
->u
.Hard
.Valid
== 0)
60 PointerPte
= MiPteToAddress(PointerPde
);
61 MiMakeSystemAddressValid(PointerPte
, Process
);
66 /* It is, skip it and move to the next PDE, unless we're done */
68 PointerPte
= MiPteToAddress(PointerPde
);
69 if (PointerPte
> LastPte
) return CommittedPages
;
72 /* Now loop all the PTEs in the range */
73 while (PointerPte
<= LastPte
)
75 /* Have we crossed a PDE boundary? */
76 if (MiIsPteOnPdeBoundary(PointerPte
))
78 /* Is this PDE demand zero? */
79 PointerPde
= MiAddressToPte(PointerPte
);
80 if (PointerPde
->u
.Long
!= 0)
82 /* It isn't -- is it valid? */
83 if (PointerPde
->u
.Hard
.Valid
== 0)
85 /* Nope, fault it in */
86 PointerPte
= MiPteToAddress(PointerPde
);
87 MiMakeSystemAddressValid(PointerPte
, Process
);
92 /* It is, skip it and move to the next PDE */
94 PointerPte
= MiPteToAddress(PointerPde
);
99 /* Is this PTE demand zero? */
100 if (PointerPte
->u
.Long
!= 0)
102 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
103 if ((PointerPte
->u
.Soft
.Protection
== MM_DECOMMIT
) &&
104 (PointerPte
->u
.Hard
.Valid
== 0) &&
105 ((PointerPte
->u
.Soft
.Prototype
== 0) ||
106 (PointerPte
->u
.Soft
.PageFileHigh
== MI_PTE_LOOKUP_NEEDED
)))
108 /* It is, so remove it from the count of commited pages */
113 /* Move to the next PTE */
117 /* Return how many committed pages there still are */
118 return CommittedPages
;
121 /* This is a non-commited VAD, so assume none of it is committed */
124 /* Is the PDE demand-zero? */
125 PointerPde
= MiAddressToPte(PointerPte
);
126 if (PointerPde
->u
.Long
!= 0)
128 /* It isn't -- is it invalid? */
129 if (PointerPde
->u
.Hard
.Valid
== 0)
131 /* It is, so page it in */
132 PointerPte
= MiPteToAddress(PointerPde
);
133 MiMakeSystemAddressValid(PointerPte
, Process
);
138 /* It is, so skip it and move to the next PDE */
140 PointerPte
= MiPteToAddress(PointerPde
);
141 if (PointerPte
> LastPte
) return CommittedPages
;
144 /* Loop all the PTEs in this PDE */
145 while (PointerPte
<= LastPte
)
147 /* Have we crossed a PDE boundary? */
148 if (MiIsPteOnPdeBoundary(PointerPte
))
150 /* Is this new PDE demand-zero? */
151 PointerPde
= MiAddressToPte(PointerPte
);
152 if (PointerPde
->u
.Long
!= 0)
154 /* It isn't. Is it valid? */
155 if (PointerPde
->u
.Hard
.Valid
== 0)
157 /* It isn't, so make it valid */
158 PointerPte
= MiPteToAddress(PointerPde
);
159 MiMakeSystemAddressValid(PointerPte
, Process
);
164 /* It is, so skip it and move to the next one */
166 PointerPte
= MiPteToAddress(PointerPde
);
171 /* Is this PTE demand-zero? */
172 if (PointerPte
->u
.Long
!= 0)
174 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
175 if ((PointerPte
->u
.Soft
.Protection
!= MM_DECOMMIT
) ||
176 (PointerPte
->u
.Hard
.Valid
== 1) ||
177 ((PointerPte
->u
.Soft
.Prototype
== 1) &&
178 (PointerPte
->u
.Soft
.PageFileHigh
!= MI_PTE_LOOKUP_NEEDED
)))
180 /* It is! So we'll treat this as a committed page */
185 /* Move to the next PTE */
189 /* Return how many committed pages we found in this VAD */
190 return CommittedPages
;
195 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress
,
196 IN PEPROCESS CurrentProcess
)
199 BOOLEAN WsWasLocked
= FALSE
, LockChange
= FALSE
;
200 PETHREAD CurrentThread
= PsGetCurrentThread();
202 /* Must be a non-pool page table, since those are double-mapped already */
203 ASSERT(PageTableVirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
204 ASSERT((PageTableVirtualAddress
< MmPagedPoolStart
) ||
205 (PageTableVirtualAddress
> MmPagedPoolEnd
));
207 /* Working set lock or PFN lock should be held */
208 ASSERT(KeAreAllApcsDisabled() == TRUE
);
210 /* Check if the page table is valid */
211 while (!MmIsAddressValid(PageTableVirtualAddress
))
213 /* Check if the WS is locked */
214 if (CurrentThread
->OwnsProcessWorkingSetExclusive
)
216 /* Unlock the working set and remember it was locked */
217 MiUnlockProcessWorkingSet(CurrentProcess
, CurrentThread
);
222 Status
= MmAccessFault(FALSE
, PageTableVirtualAddress
, KernelMode
, NULL
);
223 if (!NT_SUCCESS(Status
))
225 /* This should not fail */
226 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
229 (ULONG_PTR
)CurrentProcess
,
230 (ULONG_PTR
)PageTableVirtualAddress
);
233 /* Lock the working set again */
234 if (WsWasLocked
) MiLockProcessWorkingSet(CurrentProcess
, CurrentThread
);
236 /* This flag will be useful later when we do better locking */
240 /* Let caller know what the lock state is */
246 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress
,
250 BOOLEAN LockChange
= FALSE
;
252 /* Must be e kernel address */
253 ASSERT(VirtualAddress
> MM_HIGHEST_USER_ADDRESS
);
255 /* Check if the page is valid */
256 while (!MmIsAddressValid(VirtualAddress
))
258 /* Release the PFN database */
259 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
262 Status
= MmAccessFault(FALSE
, VirtualAddress
, KernelMode
, NULL
);
263 if (!NT_SUCCESS(Status
))
265 /* This should not fail */
266 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR
,
270 (ULONG_PTR
)VirtualAddress
);
273 /* This flag will be useful later when we do better locking */
276 /* Lock the PFN database */
277 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
280 /* Let caller know what the lock state is */
286 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
287 IN PFN_NUMBER PageCount
,
289 OUT PPFN_NUMBER ValidPages
)
291 PFN_COUNT ActualPages
= 0;
292 PETHREAD CurrentThread
= PsGetCurrentThread();
294 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
296 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
298 /* Lock the system working set */
299 MiLockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
304 /* Make sure there's some data about the page */
305 if (PointerPte
->u
.Long
)
307 /* As always, only handle current ARM3 scenarios */
308 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
309 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
311 /* Normally this is one possibility -- freeing a valid page */
312 if (PointerPte
->u
.Hard
.Valid
)
314 /* Get the page PFN */
315 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
316 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
318 /* Should not have any working set data yet */
319 ASSERT(Pfn1
->u1
.WsIndex
== 0);
321 /* Actual valid, legitimate, pages */
322 if (ValidPages
) (*ValidPages
)++;
324 /* Get the page table entry */
325 PageTableIndex
= Pfn1
->u4
.PteFrame
;
326 Pfn2
= MiGetPfnEntry(PageTableIndex
);
328 /* Lock the PFN database */
329 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
331 /* Delete it the page */
332 MI_SET_PFN_DELETED(Pfn1
);
333 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
335 /* Decrement the page table too */
336 MiDecrementShareCount(Pfn2
, PageTableIndex
);
338 /* Release the PFN database */
339 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
341 /* Destroy the PTE */
342 PointerPte
->u
.Long
= 0;
345 /* Actual legitimate pages */
351 * The only other ARM3 possibility is a demand zero page, which would
352 * mean freeing some of the paged pool pages that haven't even been
353 * touched yet, as part of a larger allocation.
355 * Right now, we shouldn't expect any page file information in the PTE
357 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
359 /* Destroy the PTE */
360 PointerPte
->u
.Long
= 0;
368 /* Release the working set */
369 MiUnlockWorkingSet(CurrentThread
, &MmSystemCacheWs
);
371 /* Flush the entire TLB */
372 KeFlushEntireTb(TRUE
, TRUE
);
380 MiDeletePte(IN PMMPTE PointerPte
,
381 IN PVOID VirtualAddress
,
382 IN PEPROCESS CurrentProcess
,
383 IN PMMPTE PrototypePte
)
387 PFN_NUMBER PageFrameIndex
;
390 /* PFN lock must be held */
391 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
393 /* Capture the PTE */
394 TempPte
= *PointerPte
;
396 /* We only support valid PTEs for now */
397 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
398 if (TempPte
.u
.Hard
.Valid
== 0)
400 /* Invalid PTEs not supported yet */
401 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
402 ASSERT(TempPte
.u
.Soft
.Transition
== 0);
405 /* Get the PFN entry */
406 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
407 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
409 /* Check if this is a valid, prototype PTE */
410 if (Pfn1
->u3
.e1
.PrototypePte
== 1)
412 /* Get the PDE and make sure it's faulted in */
413 PointerPde
= MiPteToPde(PointerPte
);
414 if (PointerPde
->u
.Hard
.Valid
== 0)
416 #if (_MI_PAGING_LEVELS == 2)
417 /* Could be paged pool access from a new process -- synchronize the page directories */
418 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress
)))
421 /* The PDE must be valid at this point */
422 KeBugCheckEx(MEMORY_MANAGEMENT
,
424 (ULONG_PTR
)PointerPte
,
426 (ULONG_PTR
)VirtualAddress
);
428 #if (_MI_PAGING_LEVELS == 2)
431 /* Drop the share count */
432 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
434 /* Either a fork, or this is the shared user data page */
435 if ((PointerPte
<= MiHighestUserPte
) && (PrototypePte
!= Pfn1
->PteAddress
))
437 /* If it's not the shared user page, then crash, since there's no fork() yet */
438 if ((PAGE_ALIGN(VirtualAddress
) != (PVOID
)USER_SHARED_DATA
) ||
439 (MmHighestUserAddress
<= (PVOID
)USER_SHARED_DATA
))
441 /* Must be some sort of memory corruption */
442 KeBugCheckEx(MEMORY_MANAGEMENT
,
444 (ULONG_PTR
)PointerPte
,
445 (ULONG_PTR
)PrototypePte
,
446 (ULONG_PTR
)Pfn1
->PteAddress
);
452 /* Make sure the saved PTE address is valid */
453 if ((PMMPTE
)((ULONG_PTR
)Pfn1
->PteAddress
& ~0x1) != PointerPte
)
455 /* The PFN entry is illegal, or invalid */
456 KeBugCheckEx(MEMORY_MANAGEMENT
,
458 (ULONG_PTR
)PointerPte
,
460 (ULONG_PTR
)Pfn1
->PteAddress
);
463 /* There should only be 1 shared reference count */
464 ASSERT(Pfn1
->u2
.ShareCount
== 1);
466 /* Drop the reference on the page table. */
467 MiDecrementShareCount(MiGetPfnEntry(Pfn1
->u4
.PteFrame
), Pfn1
->u4
.PteFrame
);
469 /* Mark the PFN for deletion and dereference what should be the last ref */
470 MI_SET_PFN_DELETED(Pfn1
);
471 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
473 /* We should eventually do this */
474 //CurrentProcess->NumberOfPrivatePages--;
477 /* Destroy the PTE and flush the TLB */
478 PointerPte
->u
.Long
= 0;
484 MiDeleteVirtualAddresses(IN ULONG_PTR Va
,
485 IN ULONG_PTR EndingAddress
,
488 PMMPTE PointerPte
, PrototypePte
, LastPrototypePte
;
491 PEPROCESS CurrentProcess
;
493 BOOLEAN AddressGap
= FALSE
;
494 PSUBSECTION Subsection
;
495 PUSHORT UsedPageTableEntries
;
497 /* Get out if this is a fake VAD, RosMm will free the marea pages */
498 if ((Vad
) && (Vad
->u
.VadFlags
.Spare
== 1)) return;
500 /* Grab the process and PTE/PDE for the address being deleted */
501 CurrentProcess
= PsGetCurrentProcess();
502 PointerPde
= MiAddressToPde(Va
);
503 PointerPte
= MiAddressToPte(Va
);
505 /* Check if this is a section VAD or a VM VAD */
506 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
) || !(Vad
->FirstPrototypePte
))
508 /* Don't worry about prototypes */
509 PrototypePte
= LastPrototypePte
= NULL
;
513 /* Get the prototype PTE */
514 PrototypePte
= Vad
->FirstPrototypePte
;
515 LastPrototypePte
= Vad
->FirstPrototypePte
+ 1;
518 /* In all cases, we don't support fork() yet */
519 ASSERT(CurrentProcess
->CloneRoot
== NULL
);
521 /* Loop the PTE for each VA */
524 /* First keep going until we find a valid PDE */
525 while (!PointerPde
->u
.Long
)
527 /* There are gaps in the address space */
530 /* Still no valid PDE, try the next 4MB (or whatever) */
533 /* Update the PTE on this new boundary */
534 PointerPte
= MiPteToAddress(PointerPde
);
536 /* Check if all the PDEs are invalid, so there's nothing to free */
537 Va
= (ULONG_PTR
)MiPteToAddress(PointerPte
);
538 if (Va
> EndingAddress
) return;
541 /* Now check if the PDE is mapped in */
542 if (!PointerPde
->u
.Hard
.Valid
)
544 /* It isn't, so map it in */
545 PointerPte
= MiPteToAddress(PointerPde
);
546 MiMakeSystemAddressValid(PointerPte
, CurrentProcess
);
549 /* Now we should have a valid PDE, mapped in, and still have some VA */
550 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
551 ASSERT(Va
<= EndingAddress
);
552 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(Va
)];
554 /* Check if this is a section VAD with gaps in it */
555 if ((AddressGap
) && (LastPrototypePte
))
557 /* We need to skip to the next correct prototype PTE */
558 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
560 /* And we need the subsection to skip to the next last prototype PTE */
561 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
565 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
569 /* No more subsections, we are done with prototype PTEs */
574 /* Lock the PFN Database while we delete the PTEs */
575 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
578 /* Capture the PDE and make sure it exists */
579 TempPte
= *PointerPte
;
582 DPRINT("Decrement used PTEs by address: %lx\n", Va
);
583 (*UsedPageTableEntries
)--;
584 ASSERT((*UsedPageTableEntries
) < PTE_COUNT
);
585 DPRINT("Refs: %lx\n", (*UsedPageTableEntries
));
587 /* Check if the PTE is actually mapped in */
588 if (TempPte
.u
.Long
& 0xFFFFFC01)
590 /* Are we dealing with section VAD? */
591 if ((LastPrototypePte
) && (PrototypePte
> LastPrototypePte
))
593 /* We need to skip to the next correct prototype PTE */
594 PrototypePte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad
, Va
>> PAGE_SHIFT
);
596 /* And we need the subsection to skip to the next last prototype PTE */
597 Subsection
= MiLocateSubsection(Vad
, Va
>> PAGE_SHIFT
);
601 LastPrototypePte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
605 /* No more subsections, we are done with prototype PTEs */
610 /* Check for prototype PTE */
611 if ((TempPte
.u
.Hard
.Valid
== 0) &&
612 (TempPte
.u
.Soft
.Prototype
== 1))
615 PointerPte
->u
.Long
= 0;
619 /* Delete the PTE proper */
620 MiDeletePte(PointerPte
,
628 /* The PTE was never mapped, just nuke it here */
629 PointerPte
->u
.Long
= 0;
633 /* Update the address and PTE for it */
638 /* Making sure the PDE is still valid */
639 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
641 while ((Va
& (PDE_MAPPED_VA
- 1)) && (Va
<= EndingAddress
));
643 /* The PDE should still be valid at this point */
644 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
646 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va
, PointerPde
->u
.Hard
.PageFrameNumber
);
647 if (!(*UsedPageTableEntries
))
649 DPRINT("They are!\n");
650 if (PointerPde
->u
.Long
!= 0)
652 DPRINT("PDE active: %lx in %16s\n", PointerPde
->u
.Hard
.PageFrameNumber
, CurrentProcess
->ImageFileName
);
654 /* Delete the PTE proper */
655 MiDeletePte(PointerPde
,
656 MiPteToAddress(PointerPde
),
662 /* Release the lock and get out if we're done */
663 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
664 if (Va
> EndingAddress
) return;
666 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
667 PointerPde
= MiAddressToPde(Va
);
673 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
674 OUT PBOOLEAN HaveBadAddress
,
675 OUT PULONG_PTR BadAddress
)
677 PEXCEPTION_RECORD ExceptionRecord
;
683 *HaveBadAddress
= FALSE
;
686 // Get the exception record
688 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
691 // Look at the exception code
693 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
694 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
695 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
698 // We can tell the address if we have more than one parameter
700 if (ExceptionRecord
->NumberParameters
> 1)
703 // Return the address
705 *HaveBadAddress
= TRUE
;
706 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
711 // Continue executing the next handler
713 return EXCEPTION_EXECUTE_HANDLER
;
718 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
719 IN PVOID SourceAddress
,
720 IN PEPROCESS TargetProcess
,
721 OUT PVOID TargetAddress
,
722 IN SIZE_T BufferSize
,
723 IN KPROCESSOR_MODE PreviousMode
,
724 OUT PSIZE_T ReturnSize
)
726 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
727 PMDL Mdl
= (PMDL
)MdlBuffer
;
728 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
729 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
730 volatile BOOLEAN PagesLocked
;
731 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
732 volatile PVOID MdlAddress
;
734 BOOLEAN HaveBadAddress
;
735 ULONG_PTR BadAddress
;
736 NTSTATUS Status
= STATUS_SUCCESS
;
740 // Calculate the maximum amount of data to move
742 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
743 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
744 CurrentSize
= TotalSize
;
745 RemainingSize
= BufferSize
;
748 // Loop as long as there is still data
750 while (RemainingSize
> 0)
753 // Check if this transfer will finish everything off
755 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
758 // Attach to the source address space
760 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
763 // Reset state for this pass
767 FailedInMoving
= FALSE
;
768 ASSERT(FailedInProbe
== FALSE
);
771 // Protect user-mode copy
776 // If this is our first time, probe the buffer
778 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
781 // Catch a failure here
783 FailedInProbe
= TRUE
;
788 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
793 FailedInProbe
= FALSE
;
797 // Initialize and probe and lock the MDL
799 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
800 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
806 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
815 // Use our SEH handler to pick this up
817 FailedInMapping
= TRUE
;
818 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
822 // Now let go of the source and grab to the target process
824 KeUnstackDetachProcess(&ApcState
);
825 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
828 // Check if this is our first time through
830 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
833 // Catch a failure here
835 FailedInProbe
= TRUE
;
840 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
845 FailedInProbe
= FALSE
;
849 // Now do the actual move
851 FailedInMoving
= TRUE
;
852 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
854 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
859 // Detach from whoever we may be attached to
861 KeUnstackDetachProcess(&ApcState
);
864 // Check if we had mapped the pages
866 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
869 // Check if we had locked the pages
871 if (PagesLocked
) MmUnlockPages(Mdl
);
874 // Check if we hit working set quota
876 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
881 return STATUS_WORKING_SET_QUOTA
;
885 // Check if we failed during the probe or mapping
887 if ((FailedInProbe
) || (FailedInMapping
))
892 Status
= _SEH2_GetExceptionCode();
893 _SEH2_YIELD(return Status
);
897 // Otherwise, we failed probably during the move
899 *ReturnSize
= BufferSize
- RemainingSize
;
903 // Check if we know exactly where we stopped copying
908 // Return the exact number of bytes copied
910 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
915 // Return partial copy
917 Status
= STATUS_PARTIAL_COPY
;
922 // Check for SEH status
924 if (Status
!= STATUS_SUCCESS
) return Status
;
927 // Detach from target
929 KeUnstackDetachProcess(&ApcState
);
934 MmUnmapLockedPages(MdlAddress
, Mdl
);
938 // Update location and size
940 RemainingSize
-= CurrentSize
;
941 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
942 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
948 *ReturnSize
= BufferSize
;
949 return STATUS_SUCCESS
;
954 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
955 IN PVOID SourceAddress
,
956 IN PEPROCESS TargetProcess
,
957 OUT PVOID TargetAddress
,
958 IN SIZE_T BufferSize
,
959 IN KPROCESSOR_MODE PreviousMode
,
960 OUT PSIZE_T ReturnSize
)
962 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
963 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
964 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
965 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
968 BOOLEAN HaveBadAddress
;
969 ULONG_PTR BadAddress
;
970 NTSTATUS Status
= STATUS_SUCCESS
;
974 // Calculate the maximum amount of data to move
976 TotalSize
= MI_MAX_TRANSFER_SIZE
;
977 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
978 CurrentSize
= TotalSize
;
979 RemainingSize
= BufferSize
;
982 // Check if we can use the stack
984 if (BufferSize
<= MI_POOL_COPY_BYTES
)
989 PoolAddress
= (PVOID
)StackBuffer
;
996 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
997 if (!PoolAddress
) ASSERT(FALSE
);
998 HavePoolAddress
= TRUE
;
1002 // Loop as long as there is still data
1004 while (RemainingSize
> 0)
1007 // Check if this transfer will finish everything off
1009 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
1012 // Attach to the source address space
1014 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
1017 // Reset state for this pass
1019 FailedInMoving
= FALSE
;
1020 ASSERT(FailedInProbe
== FALSE
);
1023 // Protect user-mode copy
1028 // If this is our first time, probe the buffer
1030 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1033 // Catch a failure here
1035 FailedInProbe
= TRUE
;
1040 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
1045 FailedInProbe
= FALSE
;
1051 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
1054 // Now let go of the source and grab to the target process
1056 KeUnstackDetachProcess(&ApcState
);
1057 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1060 // Check if this is our first time through
1062 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
1065 // Catch a failure here
1067 FailedInProbe
= TRUE
;
1072 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
1077 FailedInProbe
= FALSE
;
1081 // Now do the actual move
1083 FailedInMoving
= TRUE
;
1084 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
1086 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1091 // Detach from whoever we may be attached to
1093 KeUnstackDetachProcess(&ApcState
);
1096 // Check if we had allocated pool
1098 if (HavePoolAddress
) ExFreePool(PoolAddress
);
1101 // Check if we failed during the probe
1108 Status
= _SEH2_GetExceptionCode();
1109 _SEH2_YIELD(return Status
);
1113 // Otherwise, we failed, probably during the move
1115 *ReturnSize
= BufferSize
- RemainingSize
;
1119 // Check if we know exactly where we stopped copying
1124 // Return the exact number of bytes copied
1126 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
1131 // Return partial copy
1133 Status
= STATUS_PARTIAL_COPY
;
1138 // Check for SEH status
1140 if (Status
!= STATUS_SUCCESS
) return Status
;
1143 // Detach from target
1145 KeUnstackDetachProcess(&ApcState
);
1148 // Update location and size
1150 RemainingSize
-= CurrentSize
;
1151 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
1152 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
1157 // Check if we had allocated pool
1159 if (HavePoolAddress
) ExFreePool(PoolAddress
);
1164 *ReturnSize
= BufferSize
;
1165 return STATUS_SUCCESS
;
1170 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
1171 IN PVOID SourceAddress
,
1172 IN PEPROCESS TargetProcess
,
1173 OUT PVOID TargetAddress
,
1174 IN SIZE_T BufferSize
,
1175 IN KPROCESSOR_MODE PreviousMode
,
1176 OUT PSIZE_T ReturnSize
)
1179 PEPROCESS Process
= SourceProcess
;
1182 // Don't accept zero-sized buffers
1184 if (!BufferSize
) return STATUS_SUCCESS
;
1187 // If we are copying from ourselves, lock the target instead
1189 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
1192 // Acquire rundown protection
1194 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
1199 return STATUS_PROCESS_IS_TERMINATING
;
1203 // See if we should use the pool copy
1205 if (BufferSize
> MI_POOL_COPY_BYTES
)
1210 Status
= MiDoMappedCopy(SourceProcess
,
1223 Status
= MiDoPoolCopy(SourceProcess
,
1235 ExReleaseRundownProtection(&Process
->RundownProtect
);
1241 MmFlushVirtualMemory(IN PEPROCESS Process
,
1242 IN OUT PVOID
*BaseAddress
,
1243 IN OUT PSIZE_T RegionSize
,
1244 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1252 return STATUS_SUCCESS
;
1257 MiGetPageProtection(IN PMMPTE PointerPte
)
1263 /* Copy this PTE's contents */
1264 TempPte
= *PointerPte
;
1266 /* Assure it's not totally zero */
1267 ASSERT(TempPte
.u
.Long
);
1269 /* Check for a special prototype format */
1270 if (TempPte
.u
.Soft
.Valid
== 0 &&
1271 TempPte
.u
.Soft
.Prototype
== 1)
1273 /* Unsupported now */
1278 /* In the easy case of transition or demand zero PTE just return its protection */
1279 if (!TempPte
.u
.Hard
.Valid
) return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1281 /* If we get here, the PTE is valid, so look up the page in PFN database */
1282 Pfn
= MiGetPfnEntry(TempPte
.u
.Hard
.PageFrameNumber
);
1283 if (!Pfn
->u3
.e1
.PrototypePte
)
1285 /* Return protection of the original pte */
1286 ASSERT(Pfn
->u4
.AweAllocation
== 0);
1287 return MmProtectToValue
[Pfn
->OriginalPte
.u
.Soft
.Protection
];
1290 /* This is software PTE */
1291 DPRINT1("Prototype PTE: %lx %p\n", TempPte
.u
.Hard
.PageFrameNumber
, Pfn
);
1292 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte
));
1293 DPRINT1("Mask: %lx\n", TempPte
.u
.Soft
.Protection
);
1294 DPRINT1("Mask2: %lx\n", Pfn
->OriginalPte
.u
.Soft
.Protection
);
1295 return MmProtectToValue
[TempPte
.u
.Soft
.Protection
];
1300 MiQueryAddressState(IN PVOID Va
,
1302 IN PEPROCESS TargetProcess
,
1303 OUT PULONG ReturnedProtect
,
1310 BOOLEAN DemandZeroPte
= TRUE
, ValidPte
= FALSE
;
1311 ULONG State
= MEM_RESERVE
, Protect
= 0, LockChange
;
1312 ASSERT((Vad
->StartingVpn
<= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)) &&
1313 (Vad
->EndingVpn
>= ((ULONG_PTR
)Va
>> PAGE_SHIFT
)));
1315 /* Only normal VADs supported */
1316 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1318 /* Get the PDE and PTE for the address */
1319 PointerPde
= MiAddressToPde(Va
);
1320 PointerPte
= MiAddressToPte(Va
);
1322 /* Return the next range */
1323 *NextVa
= (PVOID
)((ULONG_PTR
)Va
+ PAGE_SIZE
);
1325 /* Loop to make sure the PDE is valid */
1331 /* Is the PDE empty? */
1332 if (!PointerPde
->u
.Long
)
1334 /* No address in this range used yet, move to the next PDE range */
1335 *NextVa
= MiPdeToAddress(PointerPde
+ 1);
1339 /* The PDE is not empty, but is it faulted in? */
1340 if (!PointerPde
->u
.Hard
.Valid
)
1342 /* It isn't, go ahead and do the fault */
1343 LockChange
= MiMakeSystemAddressValid(MiPdeToPte(PointerPde
),
1347 /* Check if the PDE was faulted in, making the PTE readable */
1348 if (!LockChange
) ValidPte
= TRUE
;
1349 } while (LockChange
);
1351 /* Is it safe to try reading the PTE? */
1354 /* FIXME: watch out for large pages */
1356 /* Capture the PTE */
1357 TempPte
= *PointerPte
;
1360 /* The PTE is valid, so it's not zeroed out */
1361 DemandZeroPte
= FALSE
;
1363 /* Check if it's valid or has a valid protection mask */
1364 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
1365 if ((TempPte
.u
.Soft
.Protection
!= MM_DECOMMIT
) ||
1366 (TempPte
.u
.Hard
.Valid
== 1))
1368 /* This means it's committed */
1371 /* Get protection state of this page */
1372 Protect
= MiGetPageProtection(PointerPte
);
1376 /* Otherwise our defaults should hold */
1377 ASSERT(Protect
== 0);
1378 ASSERT(State
== MEM_RESERVE
);
1383 /* Check if this was a demand-zero PTE, since we need to find the state */
1386 /* Check if the VAD is for committed memory */
1387 if (Vad
->u
.VadFlags
.MemCommit
)
1389 /* This is committed memory */
1392 /* Convert the protection */
1393 Protect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1397 /* Return the protection code */
1398 *ReturnedProtect
= Protect
;
1404 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle
,
1405 IN PVOID BaseAddress
,
1406 OUT PVOID MemoryInformation
,
1407 IN SIZE_T MemoryInformationLength
,
1408 OUT PSIZE_T ReturnLength
)
1410 PEPROCESS TargetProcess
;
1411 NTSTATUS Status
= STATUS_SUCCESS
;
1413 PVOID Address
, NextAddress
;
1414 BOOLEAN Found
= FALSE
;
1415 ULONG NewProtect
, NewState
;
1417 MEMORY_BASIC_INFORMATION MemoryInfo
;
1418 KAPC_STATE ApcState
;
1419 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1420 PMEMORY_AREA MemoryArea
;
1421 SIZE_T ResultLength
;
1423 /* Check for illegal addresses in user-space, or the shared memory area */
1424 if ((BaseAddress
> MM_HIGHEST_VAD_ADDRESS
) ||
1425 (PAGE_ALIGN(BaseAddress
) == (PVOID
)MM_SHARED_USER_DATA_VA
))
1427 Address
= PAGE_ALIGN(BaseAddress
);
1429 /* Make up an info structure describing this range */
1430 MemoryInfo
.BaseAddress
= Address
;
1431 MemoryInfo
.AllocationProtect
= PAGE_READONLY
;
1432 MemoryInfo
.Type
= MEM_PRIVATE
;
1434 /* Special case for shared data */
1435 if (Address
== (PVOID
)MM_SHARED_USER_DATA_VA
)
1437 MemoryInfo
.AllocationBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
1438 MemoryInfo
.State
= MEM_COMMIT
;
1439 MemoryInfo
.Protect
= PAGE_READONLY
;
1440 MemoryInfo
.RegionSize
= PAGE_SIZE
;
1444 MemoryInfo
.AllocationBase
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
1445 MemoryInfo
.State
= MEM_RESERVE
;
1446 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1447 MemoryInfo
.RegionSize
= (ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
+ 1 - (ULONG_PTR
)Address
;
1450 /* Return the data, NtQueryInformation already probed it*/
1451 if (PreviousMode
!= KernelMode
)
1455 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1456 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1458 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1460 Status
= _SEH2_GetExceptionCode();
1466 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1467 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1473 /* Check if this is for a local or remote process */
1474 if (ProcessHandle
== NtCurrentProcess())
1476 TargetProcess
= PsGetCurrentProcess();
1480 /* Reference the target process */
1481 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1482 PROCESS_QUERY_INFORMATION
,
1484 ExGetPreviousMode(),
1485 (PVOID
*)&TargetProcess
,
1487 if (!NT_SUCCESS(Status
)) return Status
;
1489 /* Attach to it now */
1490 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
1494 ASSERT(TargetProcess
->VadRoot
.NumberGenericTableElements
);
1495 if (TargetProcess
->VadRoot
.NumberGenericTableElements
)
1497 /* Scan on the right */
1498 Vad
= (PMMVAD
)TargetProcess
->VadRoot
.BalancedRoot
.RightChild
;
1499 BaseVpn
= (ULONG_PTR
)BaseAddress
>> PAGE_SHIFT
;
1502 /* Check if this VAD covers the allocation range */
1503 if ((BaseVpn
>= Vad
->StartingVpn
) &&
1504 (BaseVpn
<= Vad
->EndingVpn
))
1511 /* Check if this VAD is too high */
1512 if (BaseVpn
< Vad
->StartingVpn
)
1514 /* Stop if there is no left child */
1515 if (!Vad
->LeftChild
) break;
1517 /* Search on the left next */
1518 Vad
= Vad
->LeftChild
;
1522 /* Then this VAD is too low, keep searching on the right */
1523 ASSERT(BaseVpn
> Vad
->EndingVpn
);
1525 /* Stop if there is no right child */
1526 if (!Vad
->RightChild
) break;
1528 /* Search on the right next */
1529 Vad
= Vad
->RightChild
;
1534 /* Was a VAD found? */
1537 Address
= PAGE_ALIGN(BaseAddress
);
1539 /* Calculate region size */
1542 if (Vad
->StartingVpn
>= BaseVpn
)
1544 /* Region size is the free space till the start of that VAD */
1545 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1549 /* Get the next VAD */
1550 Vad
= (PMMVAD
)MiGetNextNode((PMMADDRESS_NODE
)Vad
);
1553 /* Region size is the free space till the start of that VAD */
1554 MemoryInfo
.RegionSize
= (ULONG_PTR
)(Vad
->StartingVpn
<< PAGE_SHIFT
) - (ULONG_PTR
)Address
;
1558 /* Maximum possible region size with that base address */
1559 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1565 /* Maximum possible region size with that base address */
1566 MemoryInfo
.RegionSize
= (PCHAR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - (PCHAR
)Address
;
1569 /* Check if we were attached */
1570 if (ProcessHandle
!= NtCurrentProcess())
1572 /* Detach and derefernece the process */
1573 KeUnstackDetachProcess(&ApcState
);
1574 ObDereferenceObject(TargetProcess
);
1577 /* Build the rest of the initial information block */
1578 MemoryInfo
.BaseAddress
= Address
;
1579 MemoryInfo
.AllocationBase
= NULL
;
1580 MemoryInfo
.AllocationProtect
= 0;
1581 MemoryInfo
.State
= MEM_FREE
;
1582 MemoryInfo
.Protect
= PAGE_NOACCESS
;
1583 MemoryInfo
.Type
= 0;
1585 /* Return the data, NtQueryInformation already probed it*/
1586 if (PreviousMode
!= KernelMode
)
1590 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1591 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1593 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1595 Status
= _SEH2_GetExceptionCode();
1601 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1602 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1608 /* Set the correct memory type based on what kind of VAD this is */
1609 if ((Vad
->u
.VadFlags
.PrivateMemory
) ||
1610 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
1612 MemoryInfo
.Type
= MEM_PRIVATE
;
1614 else if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
1616 MemoryInfo
.Type
= MEM_IMAGE
;
1620 MemoryInfo
.Type
= MEM_MAPPED
;
1623 /* Lock the address space of the process */
1624 MmLockAddressSpace(&TargetProcess
->Vm
);
1626 /* Find the memory area the specified address belongs to */
1627 MemoryArea
= MmLocateMemoryAreaByAddress(&TargetProcess
->Vm
, BaseAddress
);
1628 ASSERT(MemoryArea
!= NULL
);
1630 /* Determine information dependent on the memory area type */
1631 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1633 Status
= MmQuerySectionView(MemoryArea
, BaseAddress
, &MemoryInfo
, &ResultLength
);
1634 ASSERT(NT_SUCCESS(Status
));
1638 /* Build the initial information block */
1639 Address
= PAGE_ALIGN(BaseAddress
);
1640 MemoryInfo
.BaseAddress
= Address
;
1641 MemoryInfo
.AllocationBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
1642 MemoryInfo
.AllocationProtect
= MmProtectToValue
[Vad
->u
.VadFlags
.Protection
];
1643 MemoryInfo
.Type
= MEM_PRIVATE
;
1645 /* Find the largest chunk of memory which has the same state and protection mask */
1646 MemoryInfo
.State
= MiQueryAddressState(Address
,
1649 &MemoryInfo
.Protect
,
1651 Address
= NextAddress
;
1652 while (((ULONG_PTR
)Address
>> PAGE_SHIFT
) <= Vad
->EndingVpn
)
1654 /* Keep going unless the state or protection mask changed */
1655 NewState
= MiQueryAddressState(Address
, Vad
, TargetProcess
, &NewProtect
, &NextAddress
);
1656 if ((NewState
!= MemoryInfo
.State
) || (NewProtect
!= MemoryInfo
.Protect
)) break;
1657 Address
= NextAddress
;
1660 /* Now that we know the last VA address, calculate the region size */
1661 MemoryInfo
.RegionSize
= ((ULONG_PTR
)Address
- (ULONG_PTR
)MemoryInfo
.BaseAddress
);
1664 /* Unlock the address space of the process */
1665 MmUnlockAddressSpace(&TargetProcess
->Vm
);
1667 /* Check if we were attached */
1668 if (ProcessHandle
!= NtCurrentProcess())
1670 /* Detach and derefernece the process */
1671 KeUnstackDetachProcess(&ApcState
);
1672 ObDereferenceObject(TargetProcess
);
1675 /* Return the data, NtQueryInformation already probed it*/
1676 if (PreviousMode
!= KernelMode
)
1680 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1681 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1685 Status
= _SEH2_GetExceptionCode();
1691 *(PMEMORY_BASIC_INFORMATION
)MemoryInformation
= MemoryInfo
;
1692 if (ReturnLength
) *ReturnLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1696 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1697 "State: %lx Type: %lx Size: %lx\n",
1698 MemoryInfo
.BaseAddress
, MemoryInfo
.AllocationBase
,
1699 MemoryInfo
.AllocationProtect
, MemoryInfo
.Protect
,
1700 MemoryInfo
.State
, MemoryInfo
.Type
, MemoryInfo
.RegionSize
);
1707 MiProtectVirtualMemory(IN PEPROCESS Process
,
1708 IN OUT PVOID
*BaseAddress
,
1709 IN OUT PSIZE_T NumberOfBytesToProtect
,
1710 IN ULONG NewAccessProtection
,
1711 OUT PULONG OldAccessProtection OPTIONAL
)
1713 PMEMORY_AREA MemoryArea
;
1715 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, *BaseAddress
);
1716 if ((MemoryArea
) && (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
))
1718 return MiRosProtectVirtualMemory(Process
,
1720 NumberOfBytesToProtect
,
1721 NewAccessProtection
,
1722 OldAccessProtection
);
1726 return STATUS_CONFLICTING_ADDRESSES
;
1731 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde
,
1732 IN PEPROCESS TargetProcess
,
1735 PMMPTE PointerPte
, PointerPpe
, PointerPxe
;
1738 // Sanity checks. The latter is because we only use this function with the
1739 // PFN lock not held, so it may go away in the future.
1741 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1742 ASSERT(OldIrql
== MM_NOIRQL
);
1745 // Also get the PPE and PXE. This is okay not to #ifdef because they will
1746 // return the same address as the PDE on 2-level page table systems.
1748 // If everything is already valid, there is nothing to do.
1750 PointerPpe
= MiAddressToPte(PointerPde
);
1751 PointerPxe
= MiAddressToPde(PointerPde
);
1752 if ((PointerPxe
->u
.Hard
.Valid
) &&
1753 (PointerPpe
->u
.Hard
.Valid
) &&
1754 (PointerPde
->u
.Hard
.Valid
))
1760 // At least something is invalid, so begin by getting the PTE for the PDE itself
1761 // and then lookup each additional level. We must do it in this precise order
1762 // because the pagfault.c code (as well as in Windows) depends that the next
1763 // level up (higher) must be valid when faulting a lower level
1765 PointerPte
= MiPteToAddress(PointerPde
);
1769 // Make sure APCs continued to be disabled
1771 ASSERT(KeAreAllApcsDisabled() == TRUE
);
1774 // First, make the PXE valid if needed
1776 if (!PointerPxe
->u
.Hard
.Valid
)
1778 MiMakeSystemAddressValid(PointerPpe
, TargetProcess
);
1779 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1785 if (!PointerPpe
->u
.Hard
.Valid
)
1787 MiMakeSystemAddressValid(PointerPde
, TargetProcess
);
1788 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1792 // And finally, make the PDE itself valid.
1794 MiMakeSystemAddressValid(PointerPte
, TargetProcess
);
1797 // This should've worked the first time so the loop is really just for
1798 // show -- ASSERT that we're actually NOT going to be looping.
1800 ASSERT(PointerPxe
->u
.Hard
.Valid
== 1);
1801 ASSERT(PointerPpe
->u
.Hard
.Valid
== 1);
1802 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
1803 } while (!(PointerPxe
->u
.Hard
.Valid
) ||
1804 !(PointerPpe
->u
.Hard
.Valid
) ||
1805 !(PointerPde
->u
.Hard
.Valid
));
1810 MiProcessValidPteList(IN PMMPTE
*ValidPteList
,
1816 PFN_NUMBER PageFrameIndex
;
1820 // Acquire the PFN lock and loop all the PTEs in the list
1822 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1823 for (i
= 0; i
!= Count
; i
++)
1826 // The PTE must currently be valid
1828 TempPte
= *ValidPteList
[i
];
1829 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1832 // Get the PFN entry for the page itself, and then for its page table
1834 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
1835 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1836 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1839 // Decrement the share count on the page table, and then on the page
1842 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1843 MI_SET_PFN_DELETED(Pfn1
);
1844 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1847 // Make the page decommitted
1849 MI_WRITE_INVALID_PTE(ValidPteList
[i
], MmDecommittedPte
);
1853 // All the PTEs have been dereferenced and made invalid, flush the TLB now
1854 // and then release the PFN lock
1857 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1862 MiDecommitPages(IN PVOID StartingAddress
,
1863 IN PMMPTE EndingPte
,
1864 IN PEPROCESS Process
,
1867 PMMPTE PointerPde
, PointerPte
, CommitPte
= NULL
;
1868 ULONG CommitReduction
= 0;
1869 PMMPTE ValidPteList
[256];
1873 PUSHORT UsedPageTableEntries
;
1874 PETHREAD CurrentThread
= PsGetCurrentThread();
1877 // Get the PTE and PTE for the address, and lock the working set
1878 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
1879 // commited range ends so that we can do the right accounting.
1881 PointerPde
= MiAddressToPde(StartingAddress
);
1882 PointerPte
= MiAddressToPte(StartingAddress
);
1883 if (Vad
->u
.VadFlags
.MemCommit
) CommitPte
= MiAddressToPte(Vad
->EndingVpn
<< PAGE_SHIFT
);
1884 MiLockWorkingSet(CurrentThread
, &Process
->Vm
);
1887 // Make the PDE valid, and now loop through each page's worth of data
1889 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1890 while (PointerPte
<= EndingPte
)
1893 // Check if we've crossed a PDE boundary
1895 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
1898 // Get the new PDE and flush the valid PTEs we had built up until
1899 // now. This helps reduce the amount of TLB flushing we have to do.
1900 // Note that Windows does a much better job using timestamps and
1901 // such, and does not flush the entire TLB all the time, but right
1902 // now we have bigger problems to worry about than TLB flushing.
1904 PointerPde
= MiAddressToPde(StartingAddress
);
1907 MiProcessValidPteList(ValidPteList
, PteCount
);
1912 // Make this PDE valid
1914 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1918 // Read this PTE. It might be active or still demand-zero.
1920 PteContents
= *PointerPte
;
1921 if (PteContents
.u
.Long
)
1924 // The PTE is active. It might be valid and in a working set, or
1925 // it might be a prototype PTE or paged out or even in transition.
1927 if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
1930 // It's already decommited, so there's nothing for us to do here
1937 // Remove it from the counters, and check if it was valid or not
1939 //Process->NumberOfPrivatePages--;
1940 if (PteContents
.u
.Hard
.Valid
)
1943 // It's valid. At this point make sure that it is not a ROS
1944 // PFN. Also, we don't support ProtoPTEs in this code path.
1946 Pfn1
= MiGetPfnEntry(PteContents
.u
.Hard
.PageFrameNumber
);
1947 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
1948 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== FALSE
);
1951 // Flush any pending PTEs that we had not yet flushed, if our
1952 // list has gotten too big, then add this PTE to the flush list.
1954 if (PteCount
== 256)
1956 MiProcessValidPteList(ValidPteList
, PteCount
);
1959 ValidPteList
[PteCount
++] = PointerPte
;
1964 // We do not support any of these other scenarios at the moment
1966 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
1967 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
1968 ASSERT(PteContents
.u
.Soft
.PageFileHigh
== 0);
1971 // So the only other possibility is that it is still a demand
1972 // zero PTE, in which case we undo the accounting we did
1973 // earlier and simply make the page decommitted.
1975 //Process->NumberOfPrivatePages++;
1976 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
1983 // This used to be a zero PTE and it no longer is, so we must add a
1984 // reference to the pagetable.
1986 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(StartingAddress
)];
1987 (*UsedPageTableEntries
)++;
1988 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
1991 // Next, we account for decommitted PTEs and make the PTE as such
1993 if (PointerPte
> CommitPte
) CommitReduction
++;
1994 MI_WRITE_INVALID_PTE(PointerPte
, MmDecommittedPte
);
1998 // Move to the next PTE and the next address
2001 StartingAddress
= (PVOID
)((ULONG_PTR
)StartingAddress
+ PAGE_SIZE
);
2005 // Flush any dangling PTEs from the loop in the last page table, and then
2006 // release the working set and return the commit reduction accounting.
2008 if (PteCount
) MiProcessValidPteList(ValidPteList
, PteCount
);
2009 MiUnlockWorkingSet(CurrentThread
, &Process
->Vm
);
2010 return CommitReduction
;
2013 /* PUBLIC FUNCTIONS ***********************************************************/
2020 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
2031 MmSecureVirtualMemory(IN PVOID Address
,
2035 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2044 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
2046 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
2049 /* SYSTEM CALLS ***************************************************************/
2053 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
2054 IN PVOID BaseAddress
,
2056 IN SIZE_T NumberOfBytesToRead
,
2057 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
2059 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2061 NTSTATUS Status
= STATUS_SUCCESS
;
2062 SIZE_T BytesRead
= 0;
2066 // Check if we came from user mode
2068 if (PreviousMode
!= KernelMode
)
2071 // Validate the read addresses
2073 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
2074 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
2075 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
2076 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
2079 // Don't allow to write into kernel space
2081 return STATUS_ACCESS_VIOLATION
;
2085 // Enter SEH for probe
2090 // Probe the output value
2092 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
2094 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2097 // Get exception code
2099 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2105 // Don't do zero-byte transfers
2107 if (NumberOfBytesToRead
)
2110 // Reference the process
2112 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2118 if (NT_SUCCESS(Status
))
2123 Status
= MmCopyVirtualMemory(Process
,
2125 PsGetCurrentProcess(),
2127 NumberOfBytesToRead
,
2132 // Dereference the process
2134 ObDereferenceObject(Process
);
2139 // Check if the caller sent this parameter
2141 if (NumberOfBytesRead
)
2144 // Enter SEH to guard write
2149 // Return the number of bytes read
2151 *NumberOfBytesRead
= BytesRead
;
2153 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2167 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
2168 IN PVOID BaseAddress
,
2170 IN SIZE_T NumberOfBytesToWrite
,
2171 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
2173 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2175 NTSTATUS Status
= STATUS_SUCCESS
;
2176 SIZE_T BytesWritten
= 0;
2180 // Check if we came from user mode
2182 if (PreviousMode
!= KernelMode
)
2185 // Validate the read addresses
2187 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
2188 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
2189 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
2190 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
2193 // Don't allow to write into kernel space
2195 return STATUS_ACCESS_VIOLATION
;
2199 // Enter SEH for probe
2204 // Probe the output value
2206 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
2208 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2211 // Get exception code
2213 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2219 // Don't do zero-byte transfers
2221 if (NumberOfBytesToWrite
)
2224 // Reference the process
2226 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2232 if (NT_SUCCESS(Status
))
2237 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
2241 NumberOfBytesToWrite
,
2246 // Dereference the process
2248 ObDereferenceObject(Process
);
2253 // Check if the caller sent this parameter
2255 if (NumberOfBytesWritten
)
2258 // Enter SEH to guard write
2263 // Return the number of bytes written
2265 *NumberOfBytesWritten
= BytesWritten
;
2267 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2281 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
2282 IN OUT PVOID
*UnsafeBaseAddress
,
2283 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
2284 IN ULONG NewAccessProtection
,
2285 OUT PULONG UnsafeOldAccessProtection
)
2288 ULONG OldAccessProtection
;
2290 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2291 PVOID BaseAddress
= NULL
;
2292 SIZE_T NumberOfBytesToProtect
= 0;
2293 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2295 BOOLEAN Attached
= FALSE
;
2296 KAPC_STATE ApcState
;
2300 // Check for valid protection flags
2302 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
2303 if (Protection
!= PAGE_NOACCESS
&&
2304 Protection
!= PAGE_READONLY
&&
2305 Protection
!= PAGE_READWRITE
&&
2306 Protection
!= PAGE_WRITECOPY
&&
2307 Protection
!= PAGE_EXECUTE
&&
2308 Protection
!= PAGE_EXECUTE_READ
&&
2309 Protection
!= PAGE_EXECUTE_READWRITE
&&
2310 Protection
!= PAGE_EXECUTE_WRITECOPY
)
2315 return STATUS_INVALID_PAGE_PROTECTION
;
2319 // Check if we came from user mode
2321 if (PreviousMode
!= KernelMode
)
2324 // Enter SEH for probing
2329 // Validate all outputs
2331 ProbeForWritePointer(UnsafeBaseAddress
);
2332 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
2333 ProbeForWriteUlong(UnsafeOldAccessProtection
);
2338 BaseAddress
= *UnsafeBaseAddress
;
2339 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2341 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2344 // Get exception code
2346 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2355 BaseAddress
= *UnsafeBaseAddress
;
2356 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
2360 // Catch illegal base address
2362 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2365 // Catch illegal region size
2367 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
2372 return STATUS_INVALID_PARAMETER_3
;
2376 // 0 is also illegal
2378 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
2381 // Get a reference to the process
2383 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2384 PROCESS_VM_OPERATION
,
2389 if (!NT_SUCCESS(Status
)) return Status
;
2392 // Check if we should attach
2394 if (CurrentProcess
!= Process
)
2399 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2404 // Do the actual work
2406 Status
= MiProtectVirtualMemory(Process
,
2408 &NumberOfBytesToProtect
,
2409 NewAccessProtection
,
2410 &OldAccessProtection
);
2415 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2418 // Release reference
2420 ObDereferenceObject(Process
);
2423 // Enter SEH to return data
2428 // Return data to user
2430 *UnsafeOldAccessProtection
= OldAccessProtection
;
2431 *UnsafeBaseAddress
= BaseAddress
;
2432 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
2434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2447 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
2448 IN OUT PVOID
*BaseAddress
,
2449 IN OUT PSIZE_T NumberOfBytesToLock
,
2453 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2455 BOOLEAN Attached
= FALSE
;
2456 KAPC_STATE ApcState
;
2457 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2458 PVOID CapturedBaseAddress
;
2459 SIZE_T CapturedBytesToLock
;
2465 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2468 // Invalid set of flags
2470 return STATUS_INVALID_PARAMETER
;
2474 // At least one flag must be specified
2476 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2481 return STATUS_INVALID_PARAMETER
;
2485 // Enter SEH for probing
2490 // Validate output data
2492 ProbeForWritePointer(BaseAddress
);
2493 ProbeForWriteSize_t(NumberOfBytesToLock
);
2498 CapturedBaseAddress
= *BaseAddress
;
2499 CapturedBytesToLock
= *NumberOfBytesToLock
;
2501 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2504 // Get exception code
2506 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2511 // Catch illegal base address
2513 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2516 // Catch illegal region size
2518 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
2523 return STATUS_INVALID_PARAMETER
;
2527 // 0 is also illegal
2529 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
2532 // Get a reference to the process
2534 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2535 PROCESS_VM_OPERATION
,
2540 if (!NT_SUCCESS(Status
)) return Status
;
2543 // Check if this is is system-mapped
2545 if (MapType
& MAP_SYSTEM
)
2548 // Check for required privilege
2550 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2553 // Fail: Don't have it
2555 ObDereferenceObject(Process
);
2556 return STATUS_PRIVILEGE_NOT_HELD
;
2561 // Check if we should attach
2563 if (CurrentProcess
!= Process
)
2568 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2580 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2583 // Release reference
2585 ObDereferenceObject(Process
);
2588 // Enter SEH to return data
2593 // Return data to user
2595 *BaseAddress
= CapturedBaseAddress
;
2596 *NumberOfBytesToLock
= 0;
2598 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2601 // Get exception code
2603 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2610 return STATUS_SUCCESS
;
2615 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
2616 IN OUT PVOID
*BaseAddress
,
2617 IN OUT PSIZE_T NumberOfBytesToUnlock
,
2621 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
2623 BOOLEAN Attached
= FALSE
;
2624 KAPC_STATE ApcState
;
2625 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2626 PVOID CapturedBaseAddress
;
2627 SIZE_T CapturedBytesToUnlock
;
2633 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
2636 // Invalid set of flags
2638 return STATUS_INVALID_PARAMETER
;
2642 // At least one flag must be specified
2644 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
2649 return STATUS_INVALID_PARAMETER
;
2653 // Enter SEH for probing
2658 // Validate output data
2660 ProbeForWritePointer(BaseAddress
);
2661 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
2666 CapturedBaseAddress
= *BaseAddress
;
2667 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
2669 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2672 // Get exception code
2674 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2679 // Catch illegal base address
2681 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2684 // Catch illegal region size
2686 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
2691 return STATUS_INVALID_PARAMETER
;
2695 // 0 is also illegal
2697 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
2700 // Get a reference to the process
2702 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2703 PROCESS_VM_OPERATION
,
2708 if (!NT_SUCCESS(Status
)) return Status
;
2711 // Check if this is is system-mapped
2713 if (MapType
& MAP_SYSTEM
)
2716 // Check for required privilege
2718 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
2721 // Fail: Don't have it
2723 ObDereferenceObject(Process
);
2724 return STATUS_PRIVILEGE_NOT_HELD
;
2729 // Check if we should attach
2731 if (CurrentProcess
!= Process
)
2736 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2748 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2751 // Release reference
2753 ObDereferenceObject(Process
);
2756 // Enter SEH to return data
2761 // Return data to user
2763 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2764 *NumberOfBytesToUnlock
= 0;
2766 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2769 // Get exception code
2771 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2778 return STATUS_SUCCESS
;
2783 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
2784 IN OUT PVOID
*BaseAddress
,
2785 IN OUT PSIZE_T NumberOfBytesToFlush
,
2786 OUT PIO_STATUS_BLOCK IoStatusBlock
)
2790 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2791 PVOID CapturedBaseAddress
;
2792 SIZE_T CapturedBytesToFlush
;
2793 IO_STATUS_BLOCK LocalStatusBlock
;
2797 // Check if we came from user mode
2799 if (PreviousMode
!= KernelMode
)
2802 // Enter SEH for probing
2807 // Validate all outputs
2809 ProbeForWritePointer(BaseAddress
);
2810 ProbeForWriteSize_t(NumberOfBytesToFlush
);
2811 ProbeForWriteIoStatusBlock(IoStatusBlock
);
2816 CapturedBaseAddress
= *BaseAddress
;
2817 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2819 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2822 // Get exception code
2824 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2833 CapturedBaseAddress
= *BaseAddress
;
2834 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
2838 // Catch illegal base address
2840 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
2843 // Catch illegal region size
2845 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
2850 return STATUS_INVALID_PARAMETER
;
2854 // Get a reference to the process
2856 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2857 PROCESS_VM_OPERATION
,
2862 if (!NT_SUCCESS(Status
)) return Status
;
2867 Status
= MmFlushVirtualMemory(Process
,
2868 &CapturedBaseAddress
,
2869 &CapturedBytesToFlush
,
2873 // Release reference
2875 ObDereferenceObject(Process
);
2878 // Enter SEH to return data
2883 // Return data to user
2885 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
2886 *NumberOfBytesToFlush
= 0;
2887 *IoStatusBlock
= LocalStatusBlock
;
2889 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2905 NtGetWriteWatch(IN HANDLE ProcessHandle
,
2907 IN PVOID BaseAddress
,
2908 IN SIZE_T RegionSize
,
2909 IN PVOID
*UserAddressArray
,
2910 OUT PULONG_PTR EntriesInUserAddressArray
,
2911 OUT PULONG Granularity
)
2916 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2917 ULONG_PTR CapturedEntryCount
;
2921 // Check if we came from user mode
2923 if (PreviousMode
!= KernelMode
)
2926 // Enter SEH for probing
2931 // Catch illegal base address
2933 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
2936 // Catch illegal region size
2938 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
2943 return STATUS_INVALID_PARAMETER_3
;
2947 // Validate all data
2949 ProbeForWriteSize_t(EntriesInUserAddressArray
);
2950 ProbeForWriteUlong(Granularity
);
2955 CapturedEntryCount
= *EntriesInUserAddressArray
;
2958 // Must have a count
2960 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
2963 // Can't be larger than the maximum
2965 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
2970 return STATUS_INVALID_PARAMETER_5
;
2974 // Probe the actual array
2976 ProbeForWrite(UserAddressArray
,
2977 CapturedEntryCount
* sizeof(PVOID
),
2980 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2983 // Get exception code
2985 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2994 CapturedEntryCount
= *EntriesInUserAddressArray
;
2995 ASSERT(CapturedEntryCount
!= 0);
2999 // Check if this is a local request
3001 if (ProcessHandle
== NtCurrentProcess())
3004 // No need to reference the process
3006 Process
= PsGetCurrentProcess();
3011 // Reference the target
3013 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3014 PROCESS_VM_OPERATION
,
3019 if (!NT_SUCCESS(Status
)) return Status
;
3023 // Compute the last address and validate it
3025 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3026 if (BaseAddress
> EndAddress
)
3031 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3032 return STATUS_INVALID_PARAMETER_4
;
3041 // Dereference if needed
3043 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3046 // Enter SEH to return data
3051 // Return data to user
3053 *EntriesInUserAddressArray
= 0;
3054 *Granularity
= PAGE_SIZE
;
3056 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3059 // Get exception code
3061 Status
= _SEH2_GetExceptionCode();
3068 return STATUS_SUCCESS
;
3076 NtResetWriteWatch(IN HANDLE ProcessHandle
,
3077 IN PVOID BaseAddress
,
3078 IN SIZE_T RegionSize
)
3083 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3084 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
3087 // Catch illegal base address
3089 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
3092 // Catch illegal region size
3094 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
3099 return STATUS_INVALID_PARAMETER_3
;
3103 // Check if this is a local request
3105 if (ProcessHandle
== NtCurrentProcess())
3108 // No need to reference the process
3110 Process
= PsGetCurrentProcess();
3115 // Reference the target
3117 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3118 PROCESS_VM_OPERATION
,
3123 if (!NT_SUCCESS(Status
)) return Status
;
3127 // Compute the last address and validate it
3129 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
3130 if (BaseAddress
> EndAddress
)
3135 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3136 return STATUS_INVALID_PARAMETER_3
;
3145 // Dereference if needed
3147 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3152 return STATUS_SUCCESS
;
3157 NtQueryVirtualMemory(IN HANDLE ProcessHandle
,
3158 IN PVOID BaseAddress
,
3159 IN MEMORY_INFORMATION_CLASS MemoryInformationClass
,
3160 OUT PVOID MemoryInformation
,
3161 IN SIZE_T MemoryInformationLength
,
3162 OUT PSIZE_T ReturnLength
)
3164 NTSTATUS Status
= STATUS_SUCCESS
;
3165 KPROCESSOR_MODE PreviousMode
;
3167 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass
, BaseAddress
);
3169 /* Bail out if the address is invalid */
3170 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
3172 /* Probe return buffer */
3173 PreviousMode
= ExGetPreviousMode();
3174 if (PreviousMode
!= KernelMode
)
3178 ProbeForWrite(MemoryInformation
,
3179 MemoryInformationLength
,
3182 if (ReturnLength
) ProbeForWriteSize_t(ReturnLength
);
3184 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3186 Status
= _SEH2_GetExceptionCode();
3190 if (!NT_SUCCESS(Status
))
3196 switch(MemoryInformationClass
)
3198 case MemoryBasicInformation
:
3199 /* Validate the size information of the class */
3200 if (MemoryInformationLength
< sizeof(MEMORY_BASIC_INFORMATION
))
3202 /* The size is invalid */
3203 return STATUS_INFO_LENGTH_MISMATCH
;
3205 Status
= MiQueryMemoryBasicInformation(ProcessHandle
,
3208 MemoryInformationLength
,
3212 case MemorySectionName
:
3213 /* Validate the size information of the class */
3214 if (MemoryInformationLength
< sizeof(MEMORY_SECTION_NAME
))
3216 /* The size is invalid */
3217 return STATUS_INFO_LENGTH_MISMATCH
;
3219 Status
= MiQueryMemorySectionName(ProcessHandle
,
3222 MemoryInformationLength
,
3225 case MemoryWorkingSetList
:
3226 case MemoryBasicVlmInformation
:
3228 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass
);
3240 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
3241 IN OUT PVOID
* UBaseAddress
,
3242 IN ULONG_PTR ZeroBits
,
3243 IN OUT PSIZE_T URegionSize
,
3244 IN ULONG AllocationType
,
3248 PMEMORY_AREA MemoryArea
;
3249 PFN_NUMBER PageCount
;
3250 PMMVAD Vad
, FoundVad
;
3251 PUSHORT UsedPageTableEntries
;
3253 PMMSUPPORT AddressSpace
;
3255 ULONG_PTR PRegionSize
, StartingAddress
, EndingAddress
;
3256 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3257 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3258 PETHREAD CurrentThread
= PsGetCurrentThread();
3259 KAPC_STATE ApcState
;
3260 ULONG ProtectionMask
, QuotaCharge
= 0, QuotaFree
= 0;
3261 BOOLEAN Attached
= FALSE
, ChangeProtection
= FALSE
;
3263 PMMPTE PointerPte
, PointerPde
, LastPte
;
3266 /* Check for valid Zero bits */
3269 DPRINT1("Too many zero bits\n");
3270 return STATUS_INVALID_PARAMETER_3
;
3273 /* Check for valid Allocation Types */
3274 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
3275 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
3277 DPRINT1("Invalid Allocation Type\n");
3278 return STATUS_INVALID_PARAMETER_5
;
3281 /* Check for at least one of these Allocation Types to be set */
3282 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
3284 DPRINT1("No memory allocation base type\n");
3285 return STATUS_INVALID_PARAMETER_5
;
3288 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3289 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
3291 DPRINT1("Invalid use of MEM_RESET\n");
3292 return STATUS_INVALID_PARAMETER_5
;
3295 /* Check if large pages are being used */
3296 if (AllocationType
& MEM_LARGE_PAGES
)
3298 /* Large page allocations MUST be committed */
3299 if (!(AllocationType
& MEM_COMMIT
))
3301 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3302 return STATUS_INVALID_PARAMETER_5
;
3305 /* These flags are not allowed with large page allocations */
3306 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
3308 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3309 return STATUS_INVALID_PARAMETER_5
;
3313 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3314 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
3316 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3317 return STATUS_INVALID_PARAMETER_5
;
3320 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3321 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
3323 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3324 return STATUS_INVALID_PARAMETER_5
;
3327 /* Check for valid MEM_PHYSICAL usage */
3328 if (AllocationType
& MEM_PHYSICAL
)
3330 /* Only these flags are allowed with MEM_PHYSIAL */
3331 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
3333 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3334 return STATUS_INVALID_PARAMETER_5
;
3337 /* Then make sure PAGE_READWRITE is used */
3338 if (Protect
!= PAGE_READWRITE
)
3340 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3341 return STATUS_INVALID_PARAMETER_6
;
3346 // Force PAGE_READWRITE for everything, for now
3348 Protect
= PAGE_READWRITE
;
3350 /* Calculate the protection mask and make sure it's valid */
3351 ProtectionMask
= MiMakeProtectionMask(Protect
);
3352 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3354 DPRINT1("Invalid protection mask\n");
3355 return STATUS_INVALID_PAGE_PROTECTION
;
3361 /* Check for user-mode parameters */
3362 if (PreviousMode
!= KernelMode
)
3364 /* Make sure they are writable */
3365 ProbeForWritePointer(UBaseAddress
);
3366 ProbeForWriteUlong(URegionSize
);
3369 /* Capture their values */
3370 PBaseAddress
= *UBaseAddress
;
3371 PRegionSize
= *URegionSize
;
3373 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3375 /* Return the exception code */
3376 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3380 /* Make sure the allocation isn't past the VAD area */
3381 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
3383 DPRINT1("Virtual allocation base above User Space\n");
3384 return STATUS_INVALID_PARAMETER_2
;
3387 /* Make sure the allocation wouldn't overflow past the VAD area */
3388 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
3390 DPRINT1("Region size would overflow into kernel-memory\n");
3391 return STATUS_INVALID_PARAMETER_4
;
3394 /* Make sure there's a size specified */
3397 DPRINT1("Region size is invalid (zero)\n");
3398 return STATUS_INVALID_PARAMETER_4
;
3402 // If this is for the current process, just use PsGetCurrentProcess
3404 if (ProcessHandle
== NtCurrentProcess())
3406 Process
= CurrentProcess
;
3411 // Otherwise, reference the process with VM rights and attach to it if
3412 // this isn't the current process. We must attach because we'll be touching
3413 // PTEs and PDEs that belong to user-mode memory, and also touching the
3414 // Working Set which is stored in Hyperspace.
3416 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3417 PROCESS_VM_OPERATION
,
3422 if (!NT_SUCCESS(Status
)) return Status
;
3423 if (CurrentProcess
!= Process
)
3425 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
3431 // Check for large page allocations and make sure that the required privilege
3432 // is being held, before attempting to handle them.
3434 if ((AllocationType
& MEM_LARGE_PAGES
) &&
3435 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
)))
3437 /* Fail without it */
3438 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3439 Status
= STATUS_PRIVILEGE_NOT_HELD
;
3440 goto FailPathNoLock
;
3444 // Assert on the things we don't yet support
3446 ASSERT(ZeroBits
== 0);
3447 ASSERT((AllocationType
& MEM_LARGE_PAGES
) == 0);
3448 ASSERT((AllocationType
& MEM_PHYSICAL
) == 0);
3449 ASSERT((AllocationType
& MEM_WRITE_WATCH
) == 0);
3450 ASSERT((AllocationType
& MEM_TOP_DOWN
) == 0);
3451 ASSERT((AllocationType
& MEM_RESET
) == 0);
3452 ASSERT(Process
->VmTopDown
== 0);
3455 // Check if the caller is reserving memory, or committing memory and letting
3456 // us pick the base address
3458 if (!(PBaseAddress
) || (AllocationType
& MEM_RESERVE
))
3461 // Do not allow COPY_ON_WRITE through this API
3463 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3465 DPRINT1("Copy on write not allowed through this path\n");
3466 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3467 goto FailPathNoLock
;
3471 // Does the caller have an address in mind, or is this a blind commit?
3476 // This is a blind commit, all we need is the region size
3478 PRegionSize
= ROUND_TO_PAGES(PRegionSize
);
3479 PageCount
= BYTES_TO_PAGES(PRegionSize
);
3481 StartingAddress
= 0;
3486 // This is a reservation, so compute the starting address on the
3487 // expected 64KB granularity, and see where the ending address will
3488 // fall based on the aligned address and the passed in region size
3490 StartingAddress
= ROUND_DOWN((ULONG_PTR
)PBaseAddress
, _64K
);
3491 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3492 PageCount
= BYTES_TO_PAGES(EndingAddress
- StartingAddress
);
3496 // Allocate and initialize the VAD
3498 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'SdaV');
3499 ASSERT(Vad
!= NULL
);
3500 Vad
->u
.LongFlags
= 0;
3501 if (AllocationType
& MEM_COMMIT
) Vad
->u
.VadFlags
.MemCommit
= 1;
3502 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
3503 Vad
->u
.VadFlags
.PrivateMemory
= 1;
3504 Vad
->u
.VadFlags
.CommitCharge
= AllocationType
& MEM_COMMIT
? PageCount
: 0;
3507 // Lock the address space and make sure the process isn't already dead
3509 AddressSpace
= MmGetCurrentAddressSpace();
3510 MmLockAddressSpace(AddressSpace
);
3511 if (Process
->VmDeleted
)
3513 Status
= STATUS_PROCESS_IS_TERMINATING
;
3518 // Did we have a base address? If no, find a valid address that is 64KB
3519 // aligned in the VAD tree. Otherwise, make sure that the address range
3520 // which was passed in isn't already conflicting with an existing address
3525 Status
= MiFindEmptyAddressRangeInTree(PRegionSize
,
3528 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
3530 if (!NT_SUCCESS(Status
)) goto FailPath
;
3533 // Now we know where the allocation ends. Make sure it doesn't end up
3534 // somewhere in kernel mode.
3536 EndingAddress
= ((ULONG_PTR
)StartingAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
3537 if ((PVOID
)EndingAddress
> MM_HIGHEST_VAD_ADDRESS
)
3539 Status
= STATUS_NO_MEMORY
;
3543 else if (MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3544 EndingAddress
>> PAGE_SHIFT
,
3548 // The address specified is in conflict!
3550 Status
= STATUS_CONFLICTING_ADDRESSES
;
3555 // Write out the VAD fields for this allocation
3557 Vad
->StartingVpn
= (ULONG_PTR
)StartingAddress
>> PAGE_SHIFT
;
3558 Vad
->EndingVpn
= (ULONG_PTR
)EndingAddress
>> PAGE_SHIFT
;
3561 // FIXME: Should setup VAD bitmap
3563 Status
= STATUS_SUCCESS
;
3566 // Lock the working set and insert the VAD into the process VAD tree
3568 MiLockProcessWorkingSet(Process
, CurrentThread
);
3569 Vad
->ControlArea
= NULL
; // For Memory-Area hack
3570 MiInsertVad(Vad
, Process
);
3571 MiUnlockProcessWorkingSet(Process
, CurrentThread
);
3574 // Update the virtual size of the process, and if this is now the highest
3575 // virtual size we have ever seen, update the peak virtual size to reflect
3578 Process
->VirtualSize
+= PRegionSize
;
3579 if (Process
->VirtualSize
> Process
->PeakVirtualSize
)
3581 Process
->PeakVirtualSize
= Process
->VirtualSize
;
3585 // Release address space and detach and dereference the target process if
3586 // it was different from the current process
3588 MmUnlockAddressSpace(AddressSpace
);
3589 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3590 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3593 // Use SEH to write back the base address and the region size. In the case
3594 // of an exception, we do not return back the exception code, as the memory
3595 // *has* been allocated. The caller would now have to call VirtualQuery
3596 // or do some other similar trick to actually find out where its memory
3597 // allocation ended up
3601 *URegionSize
= PRegionSize
;
3602 *UBaseAddress
= (PVOID
)StartingAddress
;
3604 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3608 return STATUS_SUCCESS
;
3612 // This is a MEM_COMMIT on top of an existing address which must have been
3613 // MEM_RESERVED already. Compute the start and ending base addresses based
3614 // on the user input, and then compute the actual region size once all the
3615 // alignments have been done.
3617 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
3618 EndingAddress
= (((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1));
3619 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
3622 // Lock the address space and make sure the process isn't already dead
3624 AddressSpace
= MmGetCurrentAddressSpace();
3625 MmLockAddressSpace(AddressSpace
);
3626 if (Process
->VmDeleted
)
3628 DPRINT1("Process is dying\n");
3629 Status
= STATUS_PROCESS_IS_TERMINATING
;
3634 // Get the VAD for this address range, and make sure it exists
3636 FoundVad
= (PMMVAD
)MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
3637 EndingAddress
>> PAGE_SHIFT
,
3641 DPRINT1("Could not find a VAD for this allocation\n");
3642 Status
= STATUS_CONFLICTING_ADDRESSES
;
3647 // These kinds of VADs are illegal for this Windows function when trying to
3648 // commit an existing range
3650 if ((FoundVad
->u
.VadFlags
.VadType
== VadAwe
) ||
3651 (FoundVad
->u
.VadFlags
.VadType
== VadDevicePhysicalMemory
) ||
3652 (FoundVad
->u
.VadFlags
.VadType
== VadLargePages
))
3654 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
3655 Status
= STATUS_CONFLICTING_ADDRESSES
;
3660 // Make sure that this address range actually fits within the VAD for it
3662 if (((StartingAddress
>> PAGE_SHIFT
) < FoundVad
->StartingVpn
) &&
3663 ((EndingAddress
>> PAGE_SHIFT
) > FoundVad
->EndingVpn
))
3665 DPRINT1("Address range does not fit into the VAD\n");
3666 Status
= STATUS_CONFLICTING_ADDRESSES
;
3671 // If this is an existing section view, we call the old RosMm routine which
3672 // has the relevant code required to handle the section scenario. In the future
3673 // we will limit this even more so that there's almost nothing that the code
3674 // needs to do, and it will become part of section.c in RosMm
3676 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
));
3677 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
3679 return MiRosAllocateVirtualMemory(ProcessHandle
,
3690 // Is this a previously reserved section being committed? If so, enter the
3691 // special section path
3693 if (FoundVad
->u
.VadFlags
.PrivateMemory
== FALSE
)
3696 // You cannot commit large page sections through this API
3698 if (FoundVad
->u
.VadFlags
.VadType
== VadLargePageSection
)
3700 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
3701 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3706 // You can only use caching flags on a rotate VAD
3708 if ((Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)) &&
3709 (FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
))
3711 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
3712 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3717 // We should make sure that the section's permissions aren't being messed with
3719 if (FoundVad
->u
.VadFlags
.NoChange
)
3721 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
3725 // ARM3 does not support file-backed sections, only shared memory
3727 ASSERT(FoundVad
->ControlArea
->FilePointer
== NULL
);
3730 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
3732 if ((FoundVad
->u
.VadFlags
.VadType
== VadRotatePhysical
) &&
3733 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
| PAGE_NOACCESS
| PAGE_GUARD
)))
3735 DPRINT1("Invalid page protection for rotate VAD\n");
3736 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3741 // Compute PTE addresses and the quota charge, then grab the commit lock
3743 PointerPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, StartingAddress
>> PAGE_SHIFT
);
3744 LastPte
= MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad
, EndingAddress
>> PAGE_SHIFT
);
3745 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
+ 1);
3746 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
3749 // Get the segment template PTE and start looping each page
3751 TempPte
= FoundVad
->ControlArea
->Segment
->SegmentPteTemplate
;
3752 ASSERT(TempPte
.u
.Long
!= 0);
3753 while (PointerPte
<= LastPte
)
3756 // For each non-already-committed page, write the invalid template PTE
3758 if (PointerPte
->u
.Long
== 0)
3760 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3770 // Now do the commit accounting and release the lock
3772 ASSERT(QuotaCharge
>= QuotaFree
);
3773 QuotaCharge
-= QuotaFree
;
3774 FoundVad
->ControlArea
->Segment
->NumberOfCommittedPages
+= QuotaCharge
;
3775 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
3778 // We are done with committing the section pages
3780 Status
= STATUS_SUCCESS
;
3785 // This is a specific ReactOS check because we only use normal VADs
3787 ASSERT(FoundVad
->u
.VadFlags
.VadType
== VadNone
);
3790 // While this is an actual Windows check
3792 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
3795 // Throw out attempts to use copy-on-write through this API path
3797 if ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
))
3799 DPRINT1("Write copy attempted when not allowed\n");
3800 Status
= STATUS_INVALID_PAGE_PROTECTION
;
3805 // Initialize a demand-zero PTE
3808 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
3811 // Get the PTE, PDE and the last PTE for this address range
3813 PointerPde
= MiAddressToPde(StartingAddress
);
3814 PointerPte
= MiAddressToPte(StartingAddress
);
3815 LastPte
= MiAddressToPte(EndingAddress
);
3818 // Update the commit charge in the VAD as well as in the process, and check
3819 // if this commit charge was now higher than the last recorded peak, in which
3820 // case we also update the peak
3822 FoundVad
->u
.VadFlags
.CommitCharge
+= (1 + LastPte
- PointerPte
);
3823 Process
->CommitCharge
+= (1 + LastPte
- PointerPte
);
3824 if (Process
->CommitCharge
> Process
->CommitChargePeak
)
3826 Process
->CommitChargePeak
= Process
->CommitCharge
;
3830 // Lock the working set while we play with user pages and page tables
3832 //MiLockWorkingSet(CurrentThread, AddressSpace);
3835 // Make the current page table valid, and then loop each page within it
3837 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
3838 while (PointerPte
<= LastPte
)
3841 // Have we crossed into a new page table?
3843 if (!(((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)))
3846 // Get the PDE and now make it valid too
3848 PointerPde
= MiAddressToPte(PointerPte
);
3849 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
3853 // Is this a zero PTE as expected?
3855 if (PointerPte
->u
.Long
== 0)
3858 // First increment the count of pages in the page table for this
3861 UsedPageTableEntries
= &MmWorkingSetList
->UsedPageTableEntries
[MiGetPdeOffset(MiPteToAddress(PointerPte
))];
3862 (*UsedPageTableEntries
)++;
3863 ASSERT((*UsedPageTableEntries
) <= PTE_COUNT
);
3866 // And now write the invalid demand-zero PTE as requested
3868 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3870 else if (PointerPte
->u
.Long
== MmDecommittedPte
.u
.Long
)
3873 // If the PTE was already decommitted, there is nothing else to do
3874 // but to write the new demand-zero PTE
3876 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3878 else if (!(ChangeProtection
) && (Protect
!= MiGetPageProtection(PointerPte
)))
3881 // We don't handle these scenarios yet
3883 if (PointerPte
->u
.Soft
.Valid
== 0)
3885 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
3886 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
3890 // There's a change in protection, remember this for later, but do
3891 // not yet handle it.
3893 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect
);
3894 ChangeProtection
= TRUE
;
3898 // Move to the next PTE
3904 // This path is not yet handled
3906 ASSERT(ChangeProtection
== FALSE
);
3909 // Release the working set lock, unlock the address space, and detach from
3910 // the target process if it was not the current process. Also dereference the
3911 // target process if this wasn't the case.
3913 //MiUnlockProcessWorkingSet(Process, CurrentThread);
3914 Status
= STATUS_SUCCESS
;
3916 MmUnlockAddressSpace(AddressSpace
);
3918 if (Attached
) KeUnstackDetachProcess(&ApcState
);
3919 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
3922 // Use SEH to write back the base address and the region size. In the case
3923 // of an exception, we strangely do return back the exception code, even
3924 // though the memory *has* been allocated. This mimics Windows behavior and
3925 // there is not much we can do about it.
3929 *URegionSize
= PRegionSize
;
3930 *UBaseAddress
= (PVOID
)StartingAddress
;
3932 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3934 Status
= _SEH2_GetExceptionCode();
3945 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
3946 IN PVOID
* UBaseAddress
,
3947 IN PSIZE_T URegionSize
,
3950 PMEMORY_AREA MemoryArea
;
3953 ULONG_PTR CommitReduction
= 0;
3954 ULONG_PTR StartingAddress
, EndingAddress
;
3958 PMMSUPPORT AddressSpace
;
3959 PETHREAD CurrentThread
= PsGetCurrentThread();
3960 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
3961 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
3962 KAPC_STATE ApcState
;
3963 BOOLEAN Attached
= FALSE
;
3967 // Only two flags are supported
3969 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
3971 DPRINT1("Invalid FreeType\n");
3972 return STATUS_INVALID_PARAMETER_4
;
3976 // Check if no flag was used, or if both flags were used
3978 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
3979 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
3981 DPRINT1("Invalid FreeType combination\n");
3982 return STATUS_INVALID_PARAMETER_4
;
3986 // Enter SEH for probe and capture. On failure, return back to the caller
3987 // with an exception violation.
3992 // Check for user-mode parameters and make sure that they are writeable
3994 if (PreviousMode
!= KernelMode
)
3996 ProbeForWritePointer(UBaseAddress
);
3997 ProbeForWriteUlong(URegionSize
);
4001 // Capture the current values
4003 PBaseAddress
= *UBaseAddress
;
4004 PRegionSize
= *URegionSize
;
4006 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4008 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4013 // Make sure the allocation isn't past the user area
4015 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
4017 DPRINT1("Virtual free base above User Space\n");
4018 return STATUS_INVALID_PARAMETER_2
;
4022 // Make sure the allocation wouldn't overflow past the user area
4024 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
4026 DPRINT1("Region size would overflow into kernel-memory\n");
4027 return STATUS_INVALID_PARAMETER_3
;
4031 // If this is for the current process, just use PsGetCurrentProcess
4033 if (ProcessHandle
== NtCurrentProcess())
4035 Process
= CurrentProcess
;
4040 // Otherwise, reference the process with VM rights and attach to it if
4041 // this isn't the current process. We must attach because we'll be touching
4042 // PTEs and PDEs that belong to user-mode memory, and also touching the
4043 // Working Set which is stored in Hyperspace.
4045 Status
= ObReferenceObjectByHandle(ProcessHandle
,
4046 PROCESS_VM_OPERATION
,
4051 if (!NT_SUCCESS(Status
)) return Status
;
4052 if (CurrentProcess
!= Process
)
4054 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
4060 // Lock the address space
4062 AddressSpace
= MmGetCurrentAddressSpace();
4063 MmLockAddressSpace(AddressSpace
);
4066 // If the address space is being deleted, fail the de-allocation since it's
4067 // too late to do anything about it
4069 if (Process
->VmDeleted
)
4071 DPRINT1("Process is dead\n");
4072 Status
= STATUS_PROCESS_IS_TERMINATING
;
4077 // Compute start and end addresses, and locate the VAD
4079 StartingAddress
= (ULONG_PTR
)PAGE_ALIGN(PBaseAddress
);
4080 EndingAddress
= ((ULONG_PTR
)PBaseAddress
+ PRegionSize
- 1) | (PAGE_SIZE
- 1);
4081 Vad
= MiLocateAddress((PVOID
)StartingAddress
);
4084 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress
);
4085 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4090 // If the range exceeds the VAD's ending VPN, fail this request
4092 if (Vad
->EndingVpn
< (EndingAddress
>> PAGE_SHIFT
))
4094 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress
);
4095 Status
= STATUS_UNABLE_TO_FREE_VM
;
4100 // These ASSERTs are here because ReactOS ARM3 does not currently implement
4101 // any other kinds of VADs.
4103 ASSERT(Vad
->u
.VadFlags
.PrivateMemory
== 1);
4104 ASSERT(Vad
->u
.VadFlags
.NoChange
== 0);
4105 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
4108 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4109 // and that is is an ARM3 memory area, and not a section view, as we currently
4110 // don't support freeing those though this interface.
4112 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, (PVOID
)StartingAddress
);
4114 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
4117 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4119 if (FreeType
& MEM_RELEASE
)
4122 // Is the caller trying to remove the whole VAD, or remove only a portion
4123 // of it? If no region size is specified, then the assumption is that the
4124 // whole VAD is to be destroyed
4129 // The caller must specify the base address identically to the range
4130 // that is stored in the VAD.
4132 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4134 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress
);
4135 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4140 // Now compute the actual start/end addresses based on the VAD
4142 StartingAddress
= Vad
->StartingVpn
<< PAGE_SHIFT
;
4143 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4146 // Finally lock the working set and remove the VAD from the VAD tree
4148 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4149 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4150 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4155 // This means the caller wants to release a specific region within
4156 // the range. We have to find out which range this is -- the following
4157 // possibilities exist plus their union (CASE D):
4159 // STARTING ADDRESS ENDING ADDRESS
4160 // [<========][========================================][=========>]
4161 // CASE A CASE B CASE C
4164 // First, check for case A or D
4166 if ((StartingAddress
>> PAGE_SHIFT
) == Vad
->StartingVpn
)
4171 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4174 // This is the easiest one to handle -- it is identical to
4175 // the code path above when the caller sets a zero region size
4176 // and the whole VAD is destroyed
4178 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4179 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
4180 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
4185 // This case is pretty easy too -- we compute a bunch of
4186 // pages to decommit, and then push the VAD's starting address
4187 // a bit further down, then decrement the commit charge
4189 // NOT YET IMPLEMENTED IN ARM3.
4191 DPRINT1("Case A not handled\n");
4192 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4196 // After analyzing the VAD, set it to NULL so that we don't
4197 // free it in the exit path
4205 // This is case B or case C. First check for case C
4207 if ((EndingAddress
>> PAGE_SHIFT
) == Vad
->EndingVpn
)
4210 // This is pretty easy and similar to case A. We compute the
4211 // amount of pages to decommit, update the VAD's commit charge
4212 // and then change the ending address of the VAD to be a bit
4215 MiLockWorkingSet(CurrentThread
, AddressSpace
);
4216 CommitReduction
= MiCalculatePageCommitment(StartingAddress
,
4220 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4221 Vad
->EndingVpn
= ((ULONG_PTR
)StartingAddress
- 1) >> PAGE_SHIFT
;
4226 // This is case B and the hardest one. Because we are removing
4227 // a chunk of memory from the very middle of the VAD, we must
4228 // actually split the VAD into two new VADs and compute the
4229 // commit charges for each of them, and reinsert new charges.
4231 // NOT YET IMPLEMENTED IN ARM3.
4233 DPRINT1("Case B not handled\n");
4234 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4239 // After analyzing the VAD, set it to NULL so that we don't
4240 // free it in the exit path
4247 // Now we have a range of pages to dereference, so call the right API
4248 // to do that and then release the working set, since we're done messing
4249 // around with process pages.
4251 MiDeleteVirtualAddresses(StartingAddress
, EndingAddress
, NULL
);
4252 MiUnlockWorkingSet(CurrentThread
, AddressSpace
);
4253 Status
= STATUS_SUCCESS
;
4257 // Update the process counters
4259 PRegionSize
= EndingAddress
- StartingAddress
+ 1;
4260 Process
->CommitCharge
-= CommitReduction
;
4261 if (FreeType
& MEM_RELEASE
) Process
->VirtualSize
-= PRegionSize
;
4264 // Unlock the address space and free the VAD in failure cases. Next,
4265 // detach from the target process so we can write the region size and the
4266 // base address to the correct source process, and dereference the target
4269 MmUnlockAddressSpace(AddressSpace
);
4270 if (Vad
) ExFreePool(Vad
);
4271 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4272 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
4275 // Use SEH to safely return the region size and the base address of the
4276 // deallocation. If we get an access violation, don't return a failure code
4277 // as the deallocation *has* happened. The caller will just have to figure
4278 // out another way to find out where it is (such as VirtualQuery).
4282 *URegionSize
= PRegionSize
;
4283 *UBaseAddress
= (PVOID
)StartingAddress
;
4285 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
4293 // This is the decommit path. You cannot decommit from the following VADs in
4294 // Windows, so fail the vall
4296 if ((Vad
->u
.VadFlags
.VadType
== VadAwe
) ||
4297 (Vad
->u
.VadFlags
.VadType
== VadLargePages
) ||
4298 (Vad
->u
.VadFlags
.VadType
== VadRotatePhysical
))
4300 DPRINT1("Trying to decommit from invalid VAD\n");
4301 Status
= STATUS_MEMORY_NOT_ALLOCATED
;
4306 // If the caller did not specify a region size, first make sure that this
4307 // region is actually committed. If it is, then compute the ending address
4308 // based on the VAD.
4312 if (((ULONG_PTR
)PBaseAddress
>> PAGE_SHIFT
) != Vad
->StartingVpn
)
4314 DPRINT1("Decomitting non-committed memory\n");
4315 Status
= STATUS_FREE_VM_NOT_AT_BASE
;
4318 EndingAddress
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
4322 // Decommit the PTEs for the range plus the actual backing pages for the
4323 // range, then reduce that amount from the commit charge in the VAD
4325 CommitReduction
= MiAddressToPte(EndingAddress
) -
4326 MiAddressToPte(StartingAddress
) +
4328 MiDecommitPages((PVOID
)StartingAddress
,
4329 MiAddressToPte(EndingAddress
),
4332 ASSERT(CommitReduction
>= 0);
4333 Vad
->u
.VadFlags
.CommitCharge
-= CommitReduction
;
4334 ASSERT(Vad
->u
.VadFlags
.CommitCharge
>= 0);
4337 // We are done, go to the exit path without freeing the VAD as it remains
4338 // valid since we have not released the allocation.
4341 Status
= STATUS_SUCCESS
;
4345 // In the failure path, we detach and derefernece the target process, and
4346 // return whatever failure code was sent.
4349 MmUnlockAddressSpace(AddressSpace
);
4350 if (Attached
) KeUnstackDetachProcess(&ApcState
);
4351 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);