summaryrefslogtreecommitdiffstats
path: root/private/ntos/mm/lockvm.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/mm/lockvm.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/mm/lockvm.c')
-rw-r--r--private/ntos/mm/lockvm.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/private/ntos/mm/lockvm.c b/private/ntos/mm/lockvm.c
new file mode 100644
index 000000000..cf48299a2
--- /dev/null
+++ b/private/ntos/mm/lockvm.c
@@ -0,0 +1,810 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ lockvm.c
+
+Abstract:
+
+ This module contains the routines which implement the
+ NtLockVirtualMemory service.
+
+Author:
+
+ Lou Perazzoli (loup) 20-August-1989
+
+Revision History:
+
+--*/
+
+#include "mi.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtLockVirtualMemory)
+#pragma alloc_text(PAGE,NtUnlockVirtualMemory)
+#endif
+
+
+
+NTSTATUS
+NtLockVirtualMemory (
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PULONG RegionSize,
+ IN ULONG MapType
+ )
+
+/*++
+
+Routine Description:
+
+ This function locks a region of pages within the working set list
+ of a subject process.
+
+ The caller of this function must have PROCESS_VM_OPERATION access
+ to the target process. The caller must also have SeLockMemoryPrivilege.
+
+Arguments:
+
+ ProcessHandle - Supplies an open handle to a process object.
+
+ BaseAddress - The base address of the region of pages
+ to be locked. This value is rounded down to the
+ next host page address boundary.
+
+ RegionSize - A pointer to a variable that will receive
+ the actual size in bytes of the locked region of
+ pages. The initial value of this argument is
+ rounded up to the next host page size boundary.
+
+ MapType - A set of flags that describe the type of locking to
+ perform. One of MAP_PROCESS or MAP_SYSTEM.
+
+Return Value:
+
+ Returns the status
+
+ STATUS_PRIVILEGE_NOT_HELD - The caller did not have sufficient
+ privilege to perform the requested operation.
+
+ TBS
+
+
+--*/
+
+{
+ PVOID Va;
+ PVOID EndingAddress;
+ PMMPTE PointerPte;
+ PMMPTE PointerPte1;
+ PMMPFN Pfn1;
+ PMMPTE PointerPde;
+ ULONG CapturedRegionSize;
+ PVOID CapturedBase;
+ PEPROCESS TargetProcess;
+ NTSTATUS Status;
+ BOOLEAN WasLocked = FALSE;
+ KPROCESSOR_MODE PreviousMode;
+ ULONG Entry;
+ ULONG SwapEntry;
+ ULONG NumberOfAlreadyLocked;
+ ULONG NumberToLock;
+ ULONG WorkingSetIndex;
+
+ PAGED_CODE();
+
+ //
+ // Validate the flags in MapType.
+ //
+
+ if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)) != 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((MapType & (MAP_PROCESS | MAP_SYSTEM)) == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ PreviousMode = KeGetPreviousMode();
+
+ try {
+
+ if (PreviousMode != KernelMode) {
+
+ ProbeForWriteUlong ((PULONG)BaseAddress);
+ ProbeForWriteUlong (RegionSize);
+ }
+
+ //
+ // Capture the base address.
+ //
+
+ CapturedBase = *BaseAddress;
+
+ //
+ // Capture the region size.
+ //
+
+ CapturedRegionSize = *RegionSize;
+
+ } except (ExSystemExceptionFilter()) {
+
+ //
+ // If an exception occurs during the probe or capture
+ // of the initial values, then handle the exception and
+ // return the exception code as the status value.
+ //
+
+ return GetExceptionCode();
+ }
+
+ //
+ // Make sure the specified starting and ending addresses are
+ // within the user part of the virtual address space.
+ //
+
+ if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
+
+ //
+ // Invalid base address.
+ //
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((ULONG)MM_HIGHEST_USER_ADDRESS - (ULONG)CapturedBase <
+ CapturedRegionSize) {
+
+ //
+ // Invalid region size;
+ //
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ if (CapturedRegionSize == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Reference the specified process.
+ //
+
+ Status = ObReferenceObjectByHandle ( ProcessHandle,
+ PROCESS_VM_OPERATION,
+ PsProcessType,
+ PreviousMode,
+ (PVOID *)&TargetProcess,
+ NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if ((MapType & MAP_SYSTEM) != 0) {
+
+ //
+ // In addition to PROCESS_VM_OPERATION access to the target
+ // process, the caller must have SE_LOCK_MEMORY_PRIVILEGE.
+ //
+
+ if (!SeSinglePrivilegeCheck(
+ SeLockMemoryPrivilege,
+ PreviousMode
+ )) {
+
+ ObDereferenceObject( TargetProcess );
+ return( STATUS_PRIVILEGE_NOT_HELD );
+ }
+ }
+
+ //
+ // Attach to the specified process.
+ //
+
+ KeAttachProcess (&TargetProcess->Pcb);
+
+
+ //
+ // Get address creation mutex, this prevents the
+ // address range from being modified while it is examined. Raise
+ // to APC level to prevent an APC routine from acquiring the
+ // address creation mutex. Get the working set mutex so the
+ // number of already locked pages in the request can be determined.
+ //
+
+ EndingAddress = PAGE_ALIGN((ULONG)CapturedBase + CapturedRegionSize - 1);
+ Va = PAGE_ALIGN (CapturedBase);
+ NumberOfAlreadyLocked = 0;
+ NumberToLock = ((ULONG)EndingAddress - (ULONG)Va) >> PAGE_SHIFT;
+
+ LOCK_WS_AND_ADDRESS_SPACE (TargetProcess);
+
+ //
+ // Make sure the address space was not deleted, if so, return an error.
+ //
+
+ if (TargetProcess->AddressSpaceDeleted != 0) {
+ Status = STATUS_PROCESS_IS_TERMINATING;
+ goto ErrorReturn;
+ }
+
+ if (NumberToLock + MM_FLUID_WORKING_SET >
+ TargetProcess->Vm.MinimumWorkingSetSize) {
+ Status = STATUS_WORKING_SET_QUOTA;
+ goto ErrorReturn;
+ }
+
+ EndingAddress = PAGE_ALIGN((ULONG)CapturedBase + CapturedRegionSize - 1);
+ Va = PAGE_ALIGN (CapturedBase);
+
+ while (Va <= EndingAddress) {
+ if (MmIsAddressValid (Va)) {
+
+ //
+ // The page is valid, therefore it is in the working set.
+ // Locate the WSLE for the page and see if it is locked.
+ //
+
+ PointerPte1 = MiGetPteAddress (Va);
+ Pfn1 = MI_PFN_ELEMENT (PointerPte1->u.Hard.PageFrameNumber);
+
+ WorkingSetIndex = MiLocateWsle (Va,
+ MmWorkingSetList,
+ Pfn1->u1.WsIndex);
+
+ ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
+
+ if (WorkingSetIndex < MmWorkingSetList->FirstDynamic) {
+
+ //
+ // This page is locked in the working set.
+ //
+
+ NumberOfAlreadyLocked += 1;
+
+ //
+ // Check to see if the WAS_LOCKED status should be returned.
+ //
+
+ if ((MapType & MAP_PROCESS) &&
+ (MmWsle[WorkingSetIndex].u1.e1.LockedInWs == 1)) {
+ WasLocked = TRUE;
+ }
+
+ if ((MapType & MAP_SYSTEM) &&
+ (MmWsle[WorkingSetIndex].u1.e1.LockedInMemory == 1)) {
+ WasLocked = TRUE;
+ }
+ }
+ }
+ Va = (PVOID)((ULONG)Va + PAGE_SIZE);
+ }
+
+ UNLOCK_WS (TargetProcess);
+
+ //
+ // Check to ensure the working set list is still fluid after
+ // the requested number of pages are locked.
+ //
+
+ if (TargetProcess->Vm.MinimumWorkingSetSize <
+ ((MmWorkingSetList->FirstDynamic + NumberToLock +
+ MM_FLUID_WORKING_SET) - NumberOfAlreadyLocked)) {
+
+ Status = STATUS_WORKING_SET_QUOTA;
+ goto ErrorReturn1;
+ }
+
+ Va = PAGE_ALIGN (CapturedBase);
+
+ //
+ // Set up an exception handler and touch each page in the specified
+ // range.
+ //
+
+ try {
+
+ while (Va <= EndingAddress) {
+ *(volatile ULONG *)Va;
+ Va = (PVOID)((ULONG)Va + PAGE_SIZE);
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ goto ErrorReturn1;
+ }
+
+ //
+ // The complete address range is accessable, lock the pages into
+ // the working set.
+ //
+
+ PointerPte = MiGetPteAddress (CapturedBase);
+ Va = PAGE_ALIGN (CapturedBase);
+
+ //
+ // Acquire the working set mutex, no page faults are allowed.
+ //
+
+ LOCK_WS (TargetProcess);
+
+ while (Va <= EndingAddress) {
+
+ //
+ // Make sure the PDE is valid.
+ //
+
+ PointerPde = MiGetPdeAddress (Va);
+
+
+ (VOID)MiDoesPdeExistAndMakeValid(PointerPde, TargetProcess, FALSE);
+
+ //
+ // Make sure the page is in the working set.
+ //
+
+ while (PointerPte->u.Hard.Valid == 0) {
+
+ //
+ // Release the working set mutex and fault in the page.
+ //
+
+ UNLOCK_WS (TargetProcess);
+
+ //
+ // Page in the PDE and make the PTE valid.
+ //
+
+ *(volatile ULONG *)Va;
+
+ //
+ // Reacquire the working set mutex.
+ //
+
+ LOCK_WS (TargetProcess);
+
+ //
+ // Make sure the page table page is still valid. This could
+ // occur if the page that was just made valid was removed
+ // from the working set before the working set lock was
+ // acquired.
+ //
+
+ (VOID)MiDoesPdeExistAndMakeValid(PointerPde, TargetProcess, FALSE);
+ }
+
+ //
+ // The page is now in the working set, lock the page into
+ // the working set.
+ //
+
+ PointerPte1 = MiGetPteAddress (Va);
+ Pfn1 = MI_PFN_ELEMENT (PointerPte1->u.Hard.PageFrameNumber);
+
+ Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
+
+ if (Entry >= MmWorkingSetList->FirstDynamic) {
+
+ SwapEntry = MmWorkingSetList->FirstDynamic;
+
+ if (Entry != MmWorkingSetList->FirstDynamic) {
+
+ //
+ // Swap this entry with the one at first dynamic.
+ //
+
+ MiSwapWslEntries (Entry, SwapEntry, &TargetProcess->Vm);
+ }
+
+ MmWorkingSetList->FirstDynamic += 1;
+ } else {
+ SwapEntry = Entry;
+ }
+
+ //
+ // Indicate that the page is locked.
+ //
+
+ if (MapType & MAP_PROCESS) {
+ MmWsle[SwapEntry].u1.e1.LockedInWs = 1;
+ }
+
+ if (MapType & MAP_SYSTEM) {
+ MmWsle[SwapEntry].u1.e1.LockedInMemory = 1;
+ }
+
+ //
+ // Increment to the next va and PTE.
+ //
+
+ PointerPte += 1;
+ Va = (PVOID)((ULONG)Va + PAGE_SIZE);
+ if (MmWorkingSetList->NextSlot < MmWorkingSetList->FirstDynamic) {
+ MmWorkingSetList->NextSlot = MmWorkingSetList->FirstDynamic;
+ }
+ }
+
+ UNLOCK_WS (TargetProcess);
+ UNLOCK_ADDRESS_SPACE (TargetProcess);
+ KeDetachProcess();
+ ObDereferenceObject (TargetProcess);
+
+ //
+ // Update return arguments.
+ //
+
+ //
+ // Establish an exception handler and write the size and base
+ // address.
+ //
+
+ try {
+
+ *RegionSize = ((ULONG)EndingAddress - (ULONG)PAGE_ALIGN(CapturedBase)) +
+ PAGE_SIZE;
+ *BaseAddress = PAGE_ALIGN(CapturedBase);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ if (WasLocked) {
+ return STATUS_WAS_LOCKED;
+ }
+
+ return STATUS_SUCCESS;
+
+ErrorReturn:
+ UNLOCK_WS (TargetProcess);
+ErrorReturn1:
+ UNLOCK_ADDRESS_SPACE (TargetProcess);
+ KeDetachProcess();
+ ObDereferenceObject (TargetProcess);
+ return Status;
+}
+
+NTSTATUS
+NtUnlockVirtualMemory (
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PULONG RegionSize,
+ IN ULONG MapType
+ )
+
+/*++
+
+Routine Description:
+
+ This function unlocks a region of pages within the working set list
+ of a subject process.
+
+ As a side effect, any pages which are not locked and are in the
+ process's working set are removed from the process's working set.
+ This allows NtUnlockVirtualMemory to remove a range of pages
+ from the working set.
+
+ The caller of this function must have PROCESS_VM_OPERATION access
+ to the target process.
+
+ The caller must also have SeLockMemoryPrivilege for MAP_SYSTEM.
+
+Arguments:
+
+ ProcessHandle - Supplies an open handle to a process object.
+
+ BaseAddress - The base address of the region of pages
+ to be unlocked. This value is rounded down to the
+ next host page address boundary.
+
+ RegionSize - A pointer to a variable that will receive
+ the actual size in bytes of the unlocked region of
+ pages. The initial value of this argument is
+ rounded up to the next host page size boundary.
+
+ MapType - A set of flags that describe the type of unlocking to
+ perform. One of MAP_PROCESS or MAP_SYSTEM.
+
+Return Value:
+
+ Returns the status
+
+ TBS
+
+
+--*/
+
+{
+ PVOID Va;
+ PVOID EndingAddress;
+ ULONG CapturedRegionSize;
+ PVOID CapturedBase;
+ PEPROCESS TargetProcess;
+ NTSTATUS Status;
+ KPROCESSOR_MODE PreviousMode;
+ ULONG Entry;
+ PMMPTE PointerPte;
+ PMMPFN Pfn1;
+
+ PAGED_CODE();
+
+ //
+ // Validate the flags in MapType.
+ //
+
+ if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)) != 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((MapType & (MAP_PROCESS | MAP_SYSTEM)) == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ PreviousMode = KeGetPreviousMode();
+
+ try {
+
+ if (PreviousMode != KernelMode) {
+
+ ProbeForWriteUlong ((PULONG)BaseAddress);
+ ProbeForWriteUlong (RegionSize);
+ }
+
+ //
+ // Capture the base address.
+ //
+
+ CapturedBase = *BaseAddress;
+
+ //
+ // Capture the region size.
+ //
+
+ CapturedRegionSize = *RegionSize;
+
+ } except (ExSystemExceptionFilter()) {
+
+ //
+ // If an exception occurs during the probe or capture
+ // of the initial values, then handle the exception and
+ // return the exception code as the status value.
+ //
+
+ return GetExceptionCode();
+ }
+
+ //
+ // Make sure the specified starting and ending addresses are
+ // within the user part of the virtual address space.
+ //
+
+ if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
+
+ //
+ // Invalid base address.
+ //
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((ULONG)MM_HIGHEST_USER_ADDRESS - (ULONG)CapturedBase <
+ CapturedRegionSize) {
+
+ //
+ // Invalid region size;
+ //
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ if (CapturedRegionSize == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Status = ObReferenceObjectByHandle ( ProcessHandle,
+ PROCESS_VM_OPERATION,
+ PsProcessType,
+ PreviousMode,
+ (PVOID *)&TargetProcess,
+ NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if ((MapType & MAP_SYSTEM) != 0) {
+
+ //
+ // In addition to PROCESS_VM_OPERATION access to the target
+ // process, the caller must have SE_LOCK_MEMORY_PRIVILEGE.
+ //
+
+ if (!SeSinglePrivilegeCheck(
+ SeLockMemoryPrivilege,
+ PreviousMode
+ )) {
+
+ ObDereferenceObject( TargetProcess );
+ return( STATUS_PRIVILEGE_NOT_HELD );
+ }
+ }
+
+ //
+ // Attach to the specified process.
+ //
+
+ KeAttachProcess (&TargetProcess->Pcb);
+
+ //
+ // Get address creation mutex, this prevents the
+ // address range from being modified while it is examined.
+ // Block APCs so an APC routine can't get a page fault and
+ // corrupt the working set list, etc.
+ //
+
+ LOCK_WS_AND_ADDRESS_SPACE (TargetProcess);
+
+ //
+ // Make sure the address space was not deleted, if so, return an error.
+ //
+
+ if (TargetProcess->AddressSpaceDeleted != 0) {
+ Status = STATUS_PROCESS_IS_TERMINATING;
+ goto ErrorReturn;
+ }
+
+ EndingAddress = PAGE_ALIGN((ULONG)CapturedBase + CapturedRegionSize - 1);
+
+ Va = PAGE_ALIGN (CapturedBase);
+
+ while (Va <= EndingAddress) {
+
+ //
+ // Check to ensure all the specified pages are locked.
+ //
+
+ if (!MmIsAddressValid (Va)) {
+
+ //
+ // This page is not valid, therefore not in working set.
+ //
+
+ Status = STATUS_NOT_LOCKED;
+ } else {
+
+ PointerPte = MiGetPteAddress (Va);
+ ASSERT (PointerPte->u.Hard.Valid != 0);
+ Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
+ Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
+ ASSERT (Entry != WSLE_NULL_INDEX);
+
+ if ((MmWsle[Entry].u1.e1.LockedInWs == 0) &&
+ (MmWsle[Entry].u1.e1.LockedInMemory == 0)) {
+
+ //
+ // Not locked in memory or system, remove from working
+ // set.
+ //
+
+ MiTakePageFromWorkingSet (Entry,
+ &TargetProcess->Vm,
+ PointerPte);
+
+ Status = STATUS_NOT_LOCKED;
+
+ } else if (MapType & MAP_PROCESS) {
+ if (MmWsle[Entry].u1.e1.LockedInWs == 0) {
+
+ //
+ // This page is not locked.
+ //
+
+ Status = STATUS_NOT_LOCKED;
+ }
+ } else {
+ if (MmWsle[Entry].u1.e1.LockedInMemory == 0) {
+
+ //
+ // This page is not locked.
+ //
+
+ Status = STATUS_NOT_LOCKED;
+ }
+ }
+ }
+ Va = (PVOID)((ULONG)Va + PAGE_SIZE);
+ } // end while
+
+ if (Status == STATUS_NOT_LOCKED) {
+ goto ErrorReturn;
+ }
+
+ //
+ // The complete address range is locked, unlock them.
+ //
+
+ Va = PAGE_ALIGN (CapturedBase);
+
+
+ while (Va <= EndingAddress) {
+
+ PointerPte = MiGetPteAddress (Va);
+ ASSERT (PointerPte->u.Hard.Valid == 1);
+ Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
+ Entry = MiLocateWsle (Va, MmWorkingSetList, Pfn1->u1.WsIndex);
+
+ if (MapType & MAP_PROCESS) {
+ MmWsle[Entry].u1.e1.LockedInWs = 0;
+ }
+
+ if (MapType & MAP_SYSTEM) {
+ MmWsle[Entry].u1.e1.LockedInMemory = 0;
+ }
+
+ if ((MmWsle[Entry].u1.e1.LockedInMemory == 0) &&
+ MmWsle[Entry].u1.e1.LockedInWs == 0) {
+
+ //
+ // The page is no longer should be locked, move
+ // it to the dynamic part of the working set.
+ //
+
+ MmWorkingSetList->FirstDynamic -= 1;
+
+ if (Entry != MmWorkingSetList->FirstDynamic) {
+
+ //
+ // Swap this element with the last locked page, making
+ // this element the new first dynamic entry.
+ //
+
+ MiSwapWslEntries (Entry,
+ MmWorkingSetList->FirstDynamic,
+ &TargetProcess->Vm);
+ }
+ }
+
+ Va = (PVOID)((ULONG)Va + PAGE_SIZE);
+ }
+
+ UNLOCK_WS (TargetProcess);
+ UNLOCK_ADDRESS_SPACE (TargetProcess);
+ KeDetachProcess();
+ ObDereferenceObject (TargetProcess);
+
+ //
+ // Update return arguments.
+ //
+
+ //
+ // Establish an exception handler and write the size and base
+ // address.
+ //
+
+ try {
+
+ *RegionSize = ((ULONG)EndingAddress -
+ (ULONG)PAGE_ALIGN(CapturedBase)) + PAGE_SIZE;
+
+ *BaseAddress = PAGE_ALIGN(CapturedBase);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ return STATUS_SUCCESS;
+
+ErrorReturn:
+
+ UNLOCK_WS (TargetProcess);
+ UNLOCK_ADDRESS_SPACE (TargetProcess);
+ KeDetachProcess();
+ ObDereferenceObject (TargetProcess);
+ return Status;
+}
+
+