reactos/ntoskrnl/mm/anonmem.c
Timo Kreuzer 406dfdbc87 [CMAKE]
Sync trunk (rr49606)

svn path=/branches/cmake-bringup/; revision=49607
2010-11-16 13:43:39 +00:00

1285 lines
40 KiB
C

/*
* Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/mm/anonmem.c
* PURPOSE: Implementing anonymous memory.
*
* PROGRAMMERS: David Welch
* Casper Hornstrup
* KJK::Hyperion
* Ge van Geldorp
* Eric Kohl
* Royce Mitchell III
* Aleksey Bragin
* Jason Filby
* Art Yerkes
* Gunnar Andre' Dalsnes
* Filip Navara
* Thomas Weidenmueller
* Alex Ionescu
* Trevor McCort
* Steven Edwards
*/
/* INCLUDE *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#define MODULE_INVOLVED_IN_ARM3
#include "ARM3/miarm.h"
/* FUNCTIONS *****************************************************************/
NTSTATUS
NTAPI
MmWritePageVirtualMemory(PMMSUPPORT AddressSpace,
PMEMORY_AREA MemoryArea,
PVOID Address,
PMM_PAGEOP PageOp)
{
SWAPENTRY SwapEntry;
PFN_NUMBER Page;
NTSTATUS Status;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
/*
* Check for paging out from a deleted virtual memory area.
*/
if (MemoryArea->DeleteInProgress)
{
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
Page = MmGetPfnForProcess(Process, Address);
/*
* Get that the page actually is dirty.
*/
if (!MmIsDirtyPage(Process, Address))
{
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
/*
* Speculatively set the mapping to clean.
*/
MmSetCleanPage(Process, Address);
/*
* If necessary, allocate an entry in the paging file for this page
*/
SwapEntry = MmGetSavedSwapEntryPage(Page);
if (SwapEntry == 0)
{
SwapEntry = MmAllocSwapPage();
if (SwapEntry == 0)
{
MmSetDirtyPage(Process, Address);
PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
}
}
/*
* Write the page to the pagefile
*/
Status = MmWriteToSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
Status);
MmSetDirtyPage(Process, Address);
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
/*
* Otherwise we have succeeded.
*/
MmSetSavedSwapEntryPage(Page, SwapEntry);
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
NTSTATUS
NTAPI
MmPageOutVirtualMemory(PMMSUPPORT AddressSpace,
PMEMORY_AREA MemoryArea,
PVOID Address,
PMM_PAGEOP PageOp)
{
PFN_NUMBER Page;
BOOLEAN WasDirty;
SWAPENTRY SwapEntry;
NTSTATUS Status;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
Address, Process->UniqueProcessId);
/*
* Check for paging out from a deleted virtual memory area.
*/
if (MemoryArea->DeleteInProgress)
{
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
/*
* Disable the virtual mapping.
*/
MmDisableVirtualMapping(Process, Address,
&WasDirty, &Page);
if (Page == 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Paging out non-dirty data is easy.
*/
if (!WasDirty)
{
MmLockAddressSpace(AddressSpace);
MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
MmDeleteAllRmaps(Page, NULL, NULL);
if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
{
MmCreatePageFileMapping(Process, Address, SwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
}
MmUnlockAddressSpace(AddressSpace);
MmReleasePageMemoryConsumer(MC_USER, Page);
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
/*
* If necessary, allocate an entry in the paging file for this page
*/
SwapEntry = MmGetSavedSwapEntryPage(Page);
if (SwapEntry == 0)
{
SwapEntry = MmAllocSwapPage();
if (SwapEntry == 0)
{
MmShowOutOfSpaceMessagePagingFile();
MmEnableVirtualMapping(Process, Address);
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_PAGEFILE_QUOTA);
}
}
/*
* Write the page to the pagefile
*/
Status = MmWriteToSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
Status);
MmEnableVirtualMapping(Process, Address);
PageOp->Status = STATUS_UNSUCCESSFUL;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_UNSUCCESSFUL);
}
/*
* Otherwise we have succeeded, free the page
*/
DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
MmLockAddressSpace(AddressSpace);
MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
MmCreatePageFileMapping(Process, Address, SwapEntry);
MmUnlockAddressSpace(AddressSpace);
MmDeleteAllRmaps(Page, NULL, NULL);
MmSetSavedSwapEntryPage(Page, 0);
MmReleasePageMemoryConsumer(MC_USER, Page);
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
NTSTATUS
NTAPI
MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace,
MEMORY_AREA* MemoryArea,
PVOID Address,
BOOLEAN Locked)
/*
* FUNCTION: Move data into memory to satisfy a page not present fault
* ARGUMENTS:
* AddressSpace = Address space within which the fault occurred
* MemoryArea = The memory area within which the fault occurred
* Address = The absolute address of fault
* RETURNS: Status
* NOTES: This function is called with the address space lock held.
*/
{
PFN_NUMBER Page;
NTSTATUS Status;
PMM_REGION Region;
PMM_PAGEOP PageOp;
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
/*
* There is a window between taking the page fault and locking the
* address space when another thread could load the page so we check
* that.
*/
if (MmIsPagePresent(NULL, Address))
{
return(STATUS_SUCCESS);
}
/*
* Check for the virtual memory area being deleted.
*/
if (MemoryArea->DeleteInProgress)
{
return(STATUS_UNSUCCESSFUL);
}
/*
* Get the segment corresponding to the virtual address
*/
Region = MmFindRegion(MemoryArea->StartingAddress,
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
Address, NULL);
if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
{
return(STATUS_ACCESS_VIOLATION);
}
/*
* FIXME
*/
if (Region->Protect & PAGE_GUARD)
{
return(STATUS_GUARD_PAGE_VIOLATION);
}
/*
* Get or create a page operation
*/
PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
(PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
MM_PAGEOP_PAGEIN, FALSE);
if (PageOp == NULL)
{
DPRINT1("MmGetPageOp failed");
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Check if someone else is already handling this fault, if so wait
* for them
*/
if (PageOp->Thread != PsGetCurrentThread())
{
MmUnlockAddressSpace(AddressSpace);
Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
0,
KernelMode,
FALSE,
NULL);
/*
* Check for various strange conditions
*/
if (Status != STATUS_SUCCESS)
{
DPRINT1("Failed to wait for page op\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (PageOp->Status == STATUS_PENDING)
{
DPRINT1("Woke for page op before completion\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* If this wasn't a pagein then we need to restart the handling
*/
if (PageOp->OpType != MM_PAGEOP_PAGEIN)
{
MmLockAddressSpace(AddressSpace);
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_MM_RESTART_OPERATION);
}
/*
* If the thread handling this fault has failed then we don't retry
*/
if (!NT_SUCCESS(PageOp->Status))
{
MmLockAddressSpace(AddressSpace);
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
Status = PageOp->Status;
MmReleasePageOp(PageOp);
return(Status);
}
MmLockAddressSpace(AddressSpace);
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
/*
* Try to allocate a page
*/
MI_SET_USAGE(MI_USAGE_VAD);
MI_SET_PROCESS2(Process->ImageFileName);
Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
if (Status == STATUS_NO_MEMORY)
{
MmUnlockAddressSpace(AddressSpace);
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
MmLockAddressSpace(AddressSpace);
}
if (!NT_SUCCESS(Status))
{
DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* Handle swapped out pages.
*/
if (MmIsPageSwapEntry(NULL, Address))
{
SWAPENTRY SwapEntry;
MmDeletePageFileMapping(Process, Address, &SwapEntry);
Status = MmReadFromSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
MmSetSavedSwapEntryPage(Page, SwapEntry);
}
/*
* Set the page. If we fail because we are out of memory then
* try again
*/
Status = MmCreateVirtualMapping(Process,
(PVOID)PAGE_ROUND_DOWN(Address),
Region->Protect,
&Page,
1);
while (Status == STATUS_NO_MEMORY)
{
MmUnlockAddressSpace(AddressSpace);
Status = MmCreateVirtualMapping(Process,
Address,
Region->Protect,
&Page,
1);
MmLockAddressSpace(AddressSpace);
}
if (!NT_SUCCESS(Status))
{
DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
KeBugCheck(MEMORY_MANAGEMENT);
return(Status);
}
/*
* Add the page to the process's working set
*/
MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
/*
* Finish the operation
*/
PageOp->Status = STATUS_SUCCESS;
KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
MmReleasePageOp(PageOp);
return(STATUS_SUCCESS);
}
static VOID
MmModifyAttributes(PMMSUPPORT AddressSpace,
PVOID BaseAddress,
ULONG RegionSize,
ULONG OldType,
ULONG OldProtect,
ULONG NewType,
ULONG NewProtect)
/*
* FUNCTION: Modify the attributes of a memory region
*/
{
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
/*
* If we are switching a previously committed region to reserved then
* free any allocated pages within the region
*/
if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
{
ULONG i;
for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
{
PFN_NUMBER Page;
if (MmIsPageSwapEntry(Process,
(char*)BaseAddress + (i * PAGE_SIZE)))
{
SWAPENTRY SwapEntry;
MmDeletePageFileMapping(Process,
(char*)BaseAddress + (i * PAGE_SIZE),
&SwapEntry);
MmFreeSwapPage(SwapEntry);
}
else
{
MmDeleteVirtualMapping(Process,
(char*)BaseAddress + (i*PAGE_SIZE),
FALSE, NULL, &Page);
if (Page != 0)
{
SWAPENTRY SavedSwapEntry;
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
if (SavedSwapEntry != 0)
{
MmFreeSwapPage(SavedSwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
}
MmDeleteRmap(Page, Process,
(char*)BaseAddress + (i * PAGE_SIZE));
MmReleasePageMemoryConsumer(MC_USER, Page);
}
}
}
}
/*
* If we are changing the protection attributes of a committed region then
* alter the attributes for any allocated pages within the region
*/
if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
OldProtect != NewProtect)
{
ULONG i;
for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
{
if (MmIsPagePresent(Process,
(char*)BaseAddress + (i*PAGE_SIZE)))
{
MmSetPageProtect(Process,
(char*)BaseAddress + (i*PAGE_SIZE),
NewProtect);
}
}
}
}
NTSTATUS NTAPI
MiProtectVirtualMemory(IN PEPROCESS Process,
IN OUT PVOID *BaseAddress,
IN OUT PSIZE_T NumberOfBytesToProtect,
IN ULONG NewAccessProtection,
OUT PULONG OldAccessProtection OPTIONAL)
{
PMEMORY_AREA MemoryArea;
PMMSUPPORT AddressSpace;
ULONG OldAccessProtection_;
NTSTATUS Status;
*NumberOfBytesToProtect =
PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
PAGE_ROUND_DOWN(*BaseAddress);
*BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
AddressSpace = &Process->Vm;
MmLockAddressSpace(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
if (MemoryArea == NULL)
{
MmUnlockAddressSpace(AddressSpace);
return STATUS_UNSUCCESSFUL;
}
if (OldAccessProtection == NULL)
OldAccessProtection = &OldAccessProtection_;
if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
{
Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
*NumberOfBytesToProtect, NewAccessProtection,
OldAccessProtection);
}
else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
{
Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
*NumberOfBytesToProtect,
NewAccessProtection,
OldAccessProtection);
}
else
{
/* FIXME: Should we return failure or success in this case? */
Status = STATUS_CONFLICTING_ADDRESSES;
}
MmUnlockAddressSpace(AddressSpace);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
IN OUT PVOID* UBaseAddress,
IN ULONG_PTR ZeroBits,
IN OUT PSIZE_T URegionSize,
IN ULONG AllocationType,
IN ULONG Protect)
{
PEPROCESS Process;
MEMORY_AREA* MemoryArea;
ULONG_PTR MemoryAreaLength;
ULONG Type;
NTSTATUS Status;
PMMSUPPORT AddressSpace;
PVOID BaseAddress;
ULONG RegionSize;
PVOID PBaseAddress;
ULONG PRegionSize;
PHYSICAL_ADDRESS BoundaryAddressMultiple;
PEPROCESS CurrentProcess = PsGetCurrentProcess();
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
KAPC_STATE ApcState;
ULONG ProtectionMask;
BOOLEAN Attached = FALSE;
BoundaryAddressMultiple.QuadPart = 0;
PAGED_CODE();
/* Check for valid Zero bits */
if (ZeroBits > 21)
{
DPRINT1("Too many zero bits\n");
return STATUS_INVALID_PARAMETER_3;
}
/* Check for valid Allocation Types */
if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
MEM_TOP_DOWN | MEM_WRITE_WATCH)))
{
DPRINT1("Invalid Allocation Type\n");
return STATUS_INVALID_PARAMETER_5;
}
/* Check for at least one of these Allocation Types to be set */
if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
{
DPRINT1("No memory allocation base type\n");
return STATUS_INVALID_PARAMETER_5;
}
/* MEM_RESET is an exclusive flag, make sure that is valid too */
if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
{
DPRINT1("Invalid use of MEM_RESET\n");
return STATUS_INVALID_PARAMETER_5;
}
/* Check if large pages are being used */
if (AllocationType & MEM_LARGE_PAGES)
{
/* Large page allocations MUST be committed */
if (!(AllocationType & MEM_COMMIT))
{
DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
return STATUS_INVALID_PARAMETER_5;
}
/* These flags are not allowed with large page allocations */
if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
{
DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
return STATUS_INVALID_PARAMETER_5;
}
}
/* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
{
DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
return STATUS_INVALID_PARAMETER_5;
}
/* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
{
DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
return STATUS_INVALID_PARAMETER_5;
}
/* Check for valid MEM_PHYSICAL usage */
if (AllocationType & MEM_PHYSICAL)
{
/* Only these flags are allowed with MEM_PHYSIAL */
if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
{
DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
return STATUS_INVALID_PARAMETER_5;
}
/* Then make sure PAGE_READWRITE is used */
if (Protect != PAGE_READWRITE)
{
DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
return STATUS_INVALID_PARAMETER_6;
}
}
/* Calculate the protection mask and make sure it's valid */
ProtectionMask = MiMakeProtectionMask(Protect);
if (ProtectionMask == MM_INVALID_PROTECTION)
{
DPRINT1("Invalid protection mask\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
/* Enter SEH */
_SEH2_TRY
{
/* Check for user-mode parameters */
if (PreviousMode != KernelMode)
{
/* Make sure they are writable */
ProbeForWritePointer(UBaseAddress);
ProbeForWriteUlong(URegionSize);
}
/* Capture their values */
PBaseAddress = *UBaseAddress;
PRegionSize = *URegionSize;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Make sure the allocation isn't past the VAD area */
if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
{
DPRINT1("Virtual allocation base above User Space\n");
return STATUS_INVALID_PARAMETER_2;
}
/* Make sure the allocation wouldn't overflow past the VAD area */
if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
{
DPRINT1("Region size would overflow into kernel-memory\n");
return STATUS_INVALID_PARAMETER_4;
}
/* Make sure there's a size specified */
if (!PRegionSize)
{
DPRINT1("Region size is invalid (zero)\n");
return STATUS_INVALID_PARAMETER_4;
}
/* Check if this is for the current process */
if (ProcessHandle == NtCurrentProcess())
{
/* We already have the current process, no need to go through Ob */
Process = CurrentProcess;
}
else
{
/* Reference the handle for correct permissions */
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_VM_OPERATION,
PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Check if not running in the current process */
if (CurrentProcess != Process)
{
/* Attach to it */
KeStackAttachProcess(&Process->Pcb, &ApcState);
Attached = TRUE;
}
}
/* Check for large page allocations */
if (AllocationType & MEM_LARGE_PAGES)
{
/* The lock memory privilege is required */
if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
{
/* Fail without it */
DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
return STATUS_PRIVILEGE_NOT_HELD;
}
}
BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
PAGE_ROUND_DOWN(PBaseAddress);
/*
* Copy on Write is reserved for system use. This case is a certain failure
* but there may be other cases...needs more testing
*/
if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
(Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
{
DPRINT1("Copy on write is not supported by VirtualAlloc\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
DPRINT("Type %x\n", Type);
AddressSpace = &Process->Vm;
MmLockAddressSpace(AddressSpace);
if (PBaseAddress != 0)
{
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
if (MemoryArea != NULL)
{
MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
(ULONG_PTR)MemoryArea->StartingAddress;
if (((ULONG_PTR)BaseAddress + RegionSize) > (ULONG_PTR)MemoryArea->EndingAddress)
{
DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
(ULONG_PTR)BaseAddress + RegionSize, MemoryArea->EndingAddress);
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
return STATUS_MEMORY_NOT_ALLOCATED;
}
if (AllocationType == MEM_RESET)
{
if (MmIsPagePresent(Process, BaseAddress))
{
/* FIXME: mark pages as not modified */
}
else
{
/* FIXME: if pages are in paging file discard them and bring in pages of zeros */
}
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
/* MEM_RESET does not modify any attributes of region */
return STATUS_SUCCESS;
}
if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
MemoryAreaLength >= RegionSize)
{
Status =
MmAlterRegion(AddressSpace,
MemoryArea->StartingAddress,
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
BaseAddress, RegionSize,
Type, Protect, MmModifyAttributes);
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
/* Give the caller rounded BaseAddress and area length */
if (NT_SUCCESS(Status))
{
*UBaseAddress = BaseAddress;
*URegionSize = RegionSize;
DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
}
return(Status);
}
else if (MemoryAreaLength >= RegionSize)
{
/* Region list initialized? */
if (MemoryArea->Data.SectionData.RegionListHead.Flink)
{
Status =
MmAlterRegion(AddressSpace,
MemoryArea->StartingAddress,
&MemoryArea->Data.SectionData.RegionListHead,
BaseAddress, RegionSize,
Type, Protect, MmModifyAttributes);
}
else
{
Status = STATUS_ACCESS_VIOLATION;
}
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
/* Give the caller rounded BaseAddress and area length */
if (NT_SUCCESS(Status))
{
*UBaseAddress = BaseAddress;
*URegionSize = RegionSize;
DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
}
return(Status);
}
else
{
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
return(STATUS_UNSUCCESSFUL);
}
}
}
Status = MmCreateMemoryArea(AddressSpace,
MEMORY_AREA_VIRTUAL_MEMORY,
&BaseAddress,
RegionSize,
Protect,
&MemoryArea,
PBaseAddress != 0,
AllocationType & MEM_TOP_DOWN,
BoundaryAddressMultiple);
if (!NT_SUCCESS(Status))
{
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
return(Status);
}
MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
(ULONG_PTR)MemoryArea->StartingAddress;
MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
MemoryAreaLength, Type, Protect);
if ((AllocationType & MEM_COMMIT) &&
(Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
{
const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
MmReserveSwapPages(nPages);
}
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
*UBaseAddress = BaseAddress;
*URegionSize = MemoryAreaLength;
DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
return(STATUS_SUCCESS);
}
static VOID
MmFreeVirtualMemoryPage(PVOID Context,
MEMORY_AREA* MemoryArea,
PVOID Address,
PFN_NUMBER Page,
SWAPENTRY SwapEntry,
BOOLEAN Dirty)
{
PEPROCESS Process = (PEPROCESS)Context;
if (Page != 0)
{
SWAPENTRY SavedSwapEntry;
SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
if (SavedSwapEntry != 0)
{
MmFreeSwapPage(SavedSwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
}
MmDeleteRmap(Page, Process, Address);
MmReleasePageMemoryConsumer(MC_USER, Page);
}
else if (SwapEntry != 0)
{
MmFreeSwapPage(SwapEntry);
}
}
VOID
NTAPI
MmFreeVirtualMemory(PEPROCESS Process,
PMEMORY_AREA MemoryArea)
{
PLIST_ENTRY current_entry;
PMM_REGION current;
ULONG i;
DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
MemoryArea);
/* Mark this memory area as about to be deleted. */
MemoryArea->DeleteInProgress = TRUE;
/*
* Wait for any ongoing paging operations. Notice that since we have
* flagged this memory area as deleted no more page ops will be added.
*/
if (MemoryArea->PageOpCount > 0)
{
ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
(ULONG_PTR)MemoryArea->StartingAddress;
const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
{
PMM_PAGEOP PageOp;
PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
(PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
NULL, 0);
if (PageOp != NULL)
{
NTSTATUS Status;
MmUnlockAddressSpace(&Process->Vm);
Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
0,
KernelMode,
FALSE,
NULL);
if (Status != STATUS_SUCCESS)
{
DPRINT1("Failed to wait for page op\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
MmLockAddressSpace(&Process->Vm);
MmReleasePageOp(PageOp);
}
}
}
/* Free all the individual segments. */
current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
{
current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
current_entry = current_entry->Flink;
ExFreePool(current);
}
/* Actually free the memory area. */
MmFreeMemoryArea(&Process->Vm,
MemoryArea,
MmFreeVirtualMemoryPage,
(PVOID)Process);
}
/*
* @implemented
*/
NTSTATUS NTAPI
NtFreeVirtualMemory(IN HANDLE ProcessHandle,
IN PVOID* UBaseAddress,
IN PSIZE_T URegionSize,
IN ULONG FreeType)
/*
* FUNCTION: Frees a range of virtual memory
* ARGUMENTS:
* ProcessHandle = Points to the process that allocated the virtual
* memory
* BaseAddress = Points to the memory address, rounded down to a
* multiple of the pagesize
* RegionSize = Limits the range to free, rounded up to a multiple of
* the paging size
* FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
* RETURNS: Status
*/
{
MEMORY_AREA* MemoryArea;
NTSTATUS Status;
PEPROCESS Process;
PMMSUPPORT AddressSpace;
PVOID BaseAddress, PBaseAddress;
ULONG RegionSize, PRegionSize;
PEPROCESS CurrentProcess = PsGetCurrentProcess();
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
KAPC_STATE ApcState;
BOOLEAN Attached = FALSE;
PAGED_CODE();
/* Only two flags are supported */
if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
{
DPRINT1("Invalid FreeType\n");
return STATUS_INVALID_PARAMETER_4;
}
/* Check if no flag was used, or if both flags were used */
if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
{
DPRINT1("Invalid FreeType combination\n");
return STATUS_INVALID_PARAMETER_4;
}
/* Enter SEH */
_SEH2_TRY
{
/* Check for user-mode parameters */
if (PreviousMode != KernelMode)
{
/* Make sure they are writable */
ProbeForWritePointer(UBaseAddress);
ProbeForWriteUlong(URegionSize);
}
/* Capture their values */
PBaseAddress = *UBaseAddress;
PRegionSize = *URegionSize;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Make sure the allocation isn't past the user area */
if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
{
DPRINT1("Virtual free base above User Space\n");
return STATUS_INVALID_PARAMETER_2;
}
/* Make sure the allocation wouldn't overflow past the user area */
if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
{
DPRINT1("Region size would overflow into kernel-memory\n");
return STATUS_INVALID_PARAMETER_3;
}
/* Check if this is for the current process */
if (ProcessHandle == NtCurrentProcess())
{
/* We already have the current process, no need to go through Ob */
Process = CurrentProcess;
}
else
{
/* Reference the handle for correct permissions */
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_VM_OPERATION,
PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Check if not running in the current process */
if (CurrentProcess != Process)
{
/* Attach to it */
KeStackAttachProcess(&Process->Pcb, &ApcState);
Attached = TRUE;
}
}
BaseAddress = (PVOID)PAGE_ROUND_DOWN((PBaseAddress));
RegionSize = PAGE_ROUND_UP((ULONG_PTR)(PBaseAddress) + (PRegionSize)) -
PAGE_ROUND_DOWN((PBaseAddress));
AddressSpace = &Process->Vm;
MmLockAddressSpace(AddressSpace);
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
if (MemoryArea == NULL)
{
Status = STATUS_UNSUCCESSFUL;
goto unlock_deref_and_return;
}
switch (FreeType)
{
case MEM_RELEASE:
/* We can only free a memory area in one step. */
if (MemoryArea->StartingAddress != BaseAddress ||
MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
{
Status = STATUS_UNSUCCESSFUL;
goto unlock_deref_and_return;
}
MmFreeVirtualMemory(Process, MemoryArea);
Status = STATUS_SUCCESS;
goto unlock_deref_and_return;
case MEM_DECOMMIT:
Status =
MmAlterRegion(AddressSpace,
MemoryArea->StartingAddress,
(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
&MemoryArea->Data.SectionData.RegionListHead :
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
BaseAddress,
RegionSize,
MEM_RESERVE,
PAGE_NOACCESS,
MmModifyAttributes);
goto unlock_deref_and_return;
}
Status = STATUS_NOT_IMPLEMENTED;
unlock_deref_and_return:
MmUnlockAddressSpace(AddressSpace);
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
return(Status);
}
NTSTATUS
NTAPI
MmProtectAnonMem(PMMSUPPORT AddressSpace,
PMEMORY_AREA MemoryArea,
PVOID BaseAddress,
ULONG Length,
ULONG Protect,
PULONG OldProtect)
{
PMM_REGION Region;
NTSTATUS Status = STATUS_SUCCESS;
ULONG LengthCount = 0;
/* Search all Regions in MemoryArea up to Length */
/* Every Region up to Length must be committed for success */
for (;;)
{
Region = MmFindRegion(MemoryArea->StartingAddress,
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
(PVOID)((ULONG_PTR)BaseAddress + (ULONG_PTR)LengthCount), NULL);
/* If a Region was found and it is committed */
if ((Region) && (Region->Type == MEM_COMMIT))
{
LengthCount += Region->Length;
if (Length <= LengthCount) break;
continue;
}
/* If Region was found and it is not commited */
else if (Region)
{
Status = STATUS_NOT_COMMITTED;
break;
}
/* If no Region was found at all */
else if (LengthCount == 0)
{
Status = STATUS_INVALID_ADDRESS;
break;
}
}
if (NT_SUCCESS(Status))
{
*OldProtect = Region->Protect;
Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
BaseAddress, Length, Region->Type, Protect,
MmModifyAttributes);
}
return (Status);
}
NTSTATUS NTAPI
MmQueryAnonMem(PMEMORY_AREA MemoryArea,
PVOID Address,
PMEMORY_BASIC_INFORMATION Info,
PSIZE_T ResultLength)
{
PMM_REGION Region;
PVOID RegionBase = NULL;
Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
Region = MmFindRegion(MemoryArea->StartingAddress,
&MemoryArea->Data.VirtualMemoryData.RegionListHead,
Address, &RegionBase);
Info->BaseAddress = RegionBase;
Info->AllocationBase = MemoryArea->StartingAddress;
Info->AllocationProtect = MemoryArea->Protect;
Info->RegionSize = Region->Length;
Info->State = Region->Type;
Info->Protect = Region->Protect;
Info->Type = MEM_PRIVATE;
*ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
return(STATUS_SUCCESS);
}
/* EOF */