reactos/ntoskrnl/mm/marea.c
Cameron Gutman c2d0d784c7 [USB-BRINGUP-TRUNK]
- Create a branch to do a proper merge of USB work from a trunk base instead of from cmake-bringup
- In the future, DO NOT under any circumstances branch another branch. This leads to merge problems!

svn path=/branches/usb-bringup-trunk/; revision=55018
2012-01-20 20:58:46 +00:00

1069 lines
29 KiB
C

/*
* Copyright (C) 1998-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/marea.c
* PURPOSE: Implements memory areas
*
* PROGRAMMERS: Rex Jolliff
* David Welch
* Eric Kohl
* Philip Susi
* Casper Hornstrup
* Eric Kohl
* Ge van Geldorp
* Royce Mitchell III
* Aleksey Bragin
* Jason Filby
* Thomas Weidenmueller
* Gunnar Andre' Dalsnes
* Mike Nordell
* Alex Ionescu
* Filip Navara
* Herve Poussineau
* Steven Edwards
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
MEMORY_AREA MiStaticMemoryAreas[MI_STATIC_MEMORY_AREAS];
ULONG MiStaticMemoryAreaCount;
/* FUNCTIONS *****************************************************************/
/**
* @name MmIterateFirstNode
*
* @param Node
* Head node of the MEMORY_AREA tree.
*
* @return The leftmost MEMORY_AREA node (ie. the one with lowest
* address)
*/
static PMEMORY_AREA MmIterateFirstNode(PMEMORY_AREA Node)
{
while (Node->LeftChild != NULL)
Node = Node->LeftChild;
return Node;
}
/**
* @name MmIterateNextNode
*
* @param Node
* Current node in the tree.
*
* @return Next node in the tree (sorted by address).
*/
static PMEMORY_AREA MmIterateNextNode(PMEMORY_AREA Node)
{
if (Node->RightChild != NULL)
{
Node = Node->RightChild;
while (Node->LeftChild != NULL)
Node = Node->LeftChild;
}
else
{
PMEMORY_AREA TempNode = NULL;
do
{
/* Check if we're at the end of tree. */
if (Node->Parent == NULL)
return NULL;
TempNode = Node;
Node = Node->Parent;
}
while (TempNode == Node->RightChild);
}
return Node;
}
/**
* @name MmIterateLastNode
*
* @param Node
* Head node of the MEMORY_AREA tree.
*
* @return The rightmost MEMORY_AREA node (ie. the one with highest
* address)
*/
static PMEMORY_AREA MmIterateLastNode(PMEMORY_AREA Node)
{
while (Node->RightChild != NULL)
Node = Node->RightChild;
return Node;
}
/**
* @name MmIteratePreviousNode
*
* @param Node
* Current node in the tree.
*
* @return Previous node in the tree (sorted by address).
*/
static PMEMORY_AREA MmIteratePrevNode(PMEMORY_AREA Node)
{
if (Node->LeftChild != NULL)
{
Node = Node->LeftChild;
while (Node->RightChild != NULL)
Node = Node->RightChild;
}
else
{
PMEMORY_AREA TempNode = NULL;
do
{
/* Check if we're at the end of tree. */
if (Node->Parent == NULL)
return NULL;
TempNode = Node;
Node = Node->Parent;
}
while (TempNode == Node->LeftChild);
}
return Node;
}
PMEMORY_AREA NTAPI
MmLocateMemoryAreaByAddress(
PMMSUPPORT AddressSpace,
PVOID Address)
{
PMEMORY_AREA Node = (PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink;
DPRINT("MmLocateMemoryAreaByAddress(AddressSpace %p, Address %p)\n",
AddressSpace, Address);
while (Node != NULL)
{
if (Address < Node->StartingAddress)
Node = Node->LeftChild;
else if (Address >= Node->EndingAddress)
Node = Node->RightChild;
else
{
DPRINT("MmLocateMemoryAreaByAddress(%p): %p [%p - %p]\n",
Address, Node, Node->StartingAddress, Node->EndingAddress);
return Node;
}
}
DPRINT("MmLocateMemoryAreaByAddress(%p): 0\n", Address);
return NULL;
}
PMEMORY_AREA NTAPI
MmLocateMemoryAreaByRegion(
PMMSUPPORT AddressSpace,
PVOID Address,
ULONG_PTR Length)
{
PMEMORY_AREA Node;
PVOID Extent = (PVOID)((ULONG_PTR)Address + Length);
/* Special case for empty tree. */
if (AddressSpace->WorkingSetExpansionLinks.Flink == NULL)
return NULL;
/* Traverse the tree from left to right. */
for (Node = MmIterateFirstNode((PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink);
Node != NULL;
Node = MmIterateNextNode(Node))
{
if (Node->StartingAddress >= Address &&
Node->StartingAddress < Extent)
{
DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
Node->EndingAddress);
return Node;
}
if (Node->EndingAddress > Address &&
Node->EndingAddress < Extent)
{
DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
Node->EndingAddress);
return Node;
}
if (Node->StartingAddress <= Address &&
Node->EndingAddress >= Extent)
{
DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
Node->EndingAddress);
return Node;
}
if (Node->StartingAddress >= Extent)
{
DPRINT("Finished MmLocateMemoryAreaByRegion() = NULL\n");
return NULL;
}
}
return NULL;
}
/**
* @name MmCompressHelper
*
* This is helper of MmRebalanceTree. Performs a compression transformation
* count times, starting at root.
*/
static VOID
MmCompressHelper(
PMMSUPPORT AddressSpace,
ULONG Count)
{
PMEMORY_AREA Root = NULL;
PMEMORY_AREA Red = (PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink;
PMEMORY_AREA Black = Red->LeftChild;
while (Count--)
{
if (Root)
Root->LeftChild = Black;
else
AddressSpace->WorkingSetExpansionLinks.Flink = (PVOID)Black;
Black->Parent = Root;
Red->LeftChild = Black->RightChild;
if (Black->RightChild)
Black->RightChild->Parent = Red;
Black->RightChild = Red;
Red->Parent = Black;
Root = Black;
if (Count)
{
Red = Root->LeftChild;
Black = Red->LeftChild;
}
}
}
/**
* @name MmRebalanceTree
*
* Rebalance a memory area tree using the Tree->Vine->Balanced Tree
* method described in libavl documentation in chapter 4.12.
* (http://www.stanford.edu/~blp/avl/libavl.html/)
*/
static VOID
MmRebalanceTree(
PMMSUPPORT AddressSpace)
{
PMEMORY_AREA PreviousNode;
PMEMORY_AREA CurrentNode;
PMEMORY_AREA TempNode;
ULONG NodeCount = 0;
ULONG Vine; /* Number of nodes in main vine. */
ULONG Leaves; /* Nodes in incomplete bottom level, if any. */
INT Height; /* Height of produced balanced tree. */
/* Transform the tree into Vine. */
PreviousNode = NULL;
CurrentNode = (PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink;
while (CurrentNode != NULL)
{
if (CurrentNode->RightChild == NULL)
{
PreviousNode = CurrentNode;
CurrentNode = CurrentNode->LeftChild;
NodeCount++;
}
else
{
TempNode = CurrentNode->RightChild;
CurrentNode->RightChild = TempNode->LeftChild;
if (TempNode->LeftChild)
TempNode->LeftChild->Parent = CurrentNode;
TempNode->LeftChild = CurrentNode;
CurrentNode->Parent = TempNode;
CurrentNode = TempNode;
if (PreviousNode != NULL)
PreviousNode->LeftChild = TempNode;
else
AddressSpace->WorkingSetExpansionLinks.Flink = (PVOID)TempNode;
TempNode->Parent = PreviousNode;
}
}
/* Transform Vine back into a balanced tree. */
Leaves = NodeCount + 1;
for (;;)
{
ULONG Next = Leaves & (Leaves - 1);
if (Next == 0)
break;
Leaves = Next;
}
Leaves = NodeCount + 1 - Leaves;
MmCompressHelper(AddressSpace, Leaves);
Vine = NodeCount - Leaves;
Height = 1 + (Leaves > 0);
while (Vine > 1)
{
MmCompressHelper(AddressSpace, Vine / 2);
Vine /= 2;
Height++;
}
}
VOID
NTAPI
MiInsertVad(IN PMMVAD Vad,
IN PEPROCESS Process);
ULONG
NTAPI
MiMakeProtectionMask(
IN ULONG Protect
);
static VOID
MmInsertMemoryArea(
PMMSUPPORT AddressSpace,
PMEMORY_AREA marea)
{
PMEMORY_AREA Node;
PMEMORY_AREA PreviousNode;
ULONG Depth = 0;
/* Build a lame VAD if this is a user-space allocation */
if ((marea->EndingAddress < MmSystemRangeStart) && (marea->Type != MEMORY_AREA_OWNED_BY_ARM3))
{
PMMVAD Vad;
ASSERT(marea->Type == MEMORY_AREA_VIRTUAL_MEMORY || marea->Type == MEMORY_AREA_SECTION_VIEW);
Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), TAG_MVAD);
ASSERT(Vad);
RtlZeroMemory(Vad, sizeof(MMVAD));
Vad->StartingVpn = PAGE_ROUND_DOWN(marea->StartingAddress) >> PAGE_SHIFT;
/*
* For some strange reason, it is perfectly valid to create a MAREA from 0x1000 to... 0x1000.
* In a normal OS/Memory Manager, this would be retarded, but ReactOS allows this (how it works
* I don't even want to know).
*/
if (marea->EndingAddress != marea->StartingAddress)
{
Vad->EndingVpn = PAGE_ROUND_DOWN((ULONG_PTR)marea->EndingAddress - 1) >> PAGE_SHIFT;
}
else
{
Vad->EndingVpn = Vad->StartingVpn;
}
Vad->u.VadFlags.Spare = 1;
Vad->u.VadFlags.PrivateMemory = 1;
Vad->u.VadFlags.Protection = MiMakeProtectionMask(marea->Protect);
MiInsertVad(Vad, MmGetAddressSpaceOwner(AddressSpace));
marea->Vad = Vad;
}
else
{
marea->Vad = NULL;
}
if (AddressSpace->WorkingSetExpansionLinks.Flink == NULL)
{
AddressSpace->WorkingSetExpansionLinks.Flink = (PVOID)marea;
marea->LeftChild = marea->RightChild = marea->Parent = NULL;
return;
}
Node = (PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink;
do
{
DPRINT("marea->EndingAddress: %p Node->StartingAddress: %p\n",
marea->EndingAddress, Node->StartingAddress);
DPRINT("marea->StartingAddress: %p Node->EndingAddress: %p\n",
marea->StartingAddress, Node->EndingAddress);
ASSERT(marea->EndingAddress <= Node->StartingAddress ||
marea->StartingAddress >= Node->EndingAddress);
ASSERT(marea->StartingAddress != Node->StartingAddress);
PreviousNode = Node;
if (marea->StartingAddress < Node->StartingAddress)
Node = Node->LeftChild;
else
Node = Node->RightChild;
if (Node)
{
Depth++;
if (Depth == 22)
{
MmRebalanceTree(AddressSpace);
PreviousNode = Node->Parent;
}
}
}
while (Node != NULL);
marea->LeftChild = marea->RightChild = NULL;
marea->Parent = PreviousNode;
if (marea->StartingAddress < PreviousNode->StartingAddress)
PreviousNode->LeftChild = marea;
else
PreviousNode->RightChild = marea;
}
static PVOID
MmFindGapBottomUp(
PMMSUPPORT AddressSpace,
ULONG_PTR Length,
ULONG_PTR Granularity)
{
PVOID LowestAddress = MmGetAddressSpaceOwner(AddressSpace) ? MM_LOWEST_USER_ADDRESS : MmSystemRangeStart;
PVOID HighestAddress = MmGetAddressSpaceOwner(AddressSpace) ?
MmHighestUserAddress : (PVOID)MAXULONG_PTR;
PVOID AlignedAddress;
PMEMORY_AREA Node;
PMEMORY_AREA FirstNode;
PMEMORY_AREA PreviousNode;
DPRINT("LowestAddress: %p HighestAddress: %p\n",
LowestAddress, HighestAddress);
AlignedAddress = MM_ROUND_UP(LowestAddress, Granularity);
/* Special case for empty tree. */
if (AddressSpace->WorkingSetExpansionLinks.Flink == NULL)
{
if ((ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length)
{
DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
return AlignedAddress;
}
DPRINT("MmFindGapBottomUp: 0\n");
return 0;
}
/* Go to the node with lowest address in the tree. */
FirstNode = Node = MmIterateFirstNode((PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink);
/* Traverse the tree from left to right. */
PreviousNode = Node;
for (;;)
{
Node = MmIterateNextNode(Node);
if (Node == NULL)
break;
AlignedAddress = MM_ROUND_UP(PreviousNode->EndingAddress, Granularity);
if (AlignedAddress >= LowestAddress)
{
if (Node->StartingAddress > AlignedAddress &&
(ULONG_PTR)Node->StartingAddress - (ULONG_PTR)AlignedAddress >= Length)
{
DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
ASSERT(AlignedAddress >= LowestAddress);
return AlignedAddress;
}
}
PreviousNode = Node;
}
/* Check if there is enough space after the last memory area. */
AlignedAddress = MM_ROUND_UP(PreviousNode->EndingAddress, Granularity);
if ((ULONG_PTR)HighestAddress > (ULONG_PTR)AlignedAddress &&
(ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length)
{
DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
ASSERT(AlignedAddress >= LowestAddress);
return AlignedAddress;
}
/* Check if there is enough space before the first memory area. */
AlignedAddress = MM_ROUND_UP(LowestAddress, Granularity);
if (FirstNode->StartingAddress > AlignedAddress &&
(ULONG_PTR)FirstNode->StartingAddress - (ULONG_PTR)AlignedAddress >= Length)
{
DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
ASSERT(AlignedAddress >= LowestAddress);
return AlignedAddress;
}
DPRINT("MmFindGapBottomUp: 0\n");
return 0;
}
static PVOID
MmFindGapTopDown(
PMMSUPPORT AddressSpace,
ULONG_PTR Length,
ULONG_PTR Granularity)
{
PVOID LowestAddress = MmGetAddressSpaceOwner(AddressSpace) ? MM_LOWEST_USER_ADDRESS : MmSystemRangeStart;
PVOID HighestAddress = MmGetAddressSpaceOwner(AddressSpace) ?
(PVOID)((ULONG_PTR)MmSystemRangeStart - 1) : (PVOID)MAXULONG_PTR;
PVOID AlignedAddress;
PMEMORY_AREA Node;
PMEMORY_AREA PreviousNode;
DPRINT("LowestAddress: %p HighestAddress: %p\n",
LowestAddress, HighestAddress);
AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)HighestAddress - Length + 1, Granularity);
/* Check for overflow. */
if (AlignedAddress > HighestAddress)
return NULL;
/* Special case for empty tree. */
if (AddressSpace->WorkingSetExpansionLinks.Flink == NULL)
{
if (AlignedAddress >= LowestAddress)
{
DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
return AlignedAddress;
}
DPRINT("MmFindGapTopDown: 0\n");
return 0;
}
/* Go to the node with highest address in the tree. */
Node = MmIterateLastNode((PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink);
/* Check if there is enough space after the last memory area. */
if (Node->EndingAddress <= AlignedAddress)
{
DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
return AlignedAddress;
}
/* Traverse the tree from left to right. */
PreviousNode = Node;
for (;;)
{
Node = MmIteratePrevNode(Node);
if (Node == NULL)
break;
AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)PreviousNode->StartingAddress - Length + 1, Granularity);
/* Check for overflow. */
if (AlignedAddress > PreviousNode->StartingAddress)
return NULL;
if (Node->EndingAddress <= AlignedAddress)
{
DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
return AlignedAddress;
}
PreviousNode = Node;
}
AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)PreviousNode->StartingAddress - Length + 1, Granularity);
/* Check for overflow. */
if (AlignedAddress > PreviousNode->StartingAddress)
return NULL;
if (AlignedAddress >= LowestAddress)
{
DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
return AlignedAddress;
}
DPRINT("MmFindGapTopDown: 0\n");
return 0;
}
PVOID NTAPI
MmFindGap(
PMMSUPPORT AddressSpace,
ULONG_PTR Length,
ULONG_PTR Granularity,
BOOLEAN TopDown)
{
if (TopDown)
return MmFindGapTopDown(AddressSpace, Length, Granularity);
return MmFindGapBottomUp(AddressSpace, Length, Granularity);
}
ULONG_PTR NTAPI
MmFindGapAtAddress(
PMMSUPPORT AddressSpace,
PVOID Address)
{
PMEMORY_AREA Node = (PMEMORY_AREA)AddressSpace->WorkingSetExpansionLinks.Flink;
PMEMORY_AREA RightNeighbour = NULL;
PVOID LowestAddress = MmGetAddressSpaceOwner(AddressSpace) ? MM_LOWEST_USER_ADDRESS : MmSystemRangeStart;
PVOID HighestAddress = MmGetAddressSpaceOwner(AddressSpace) ?
(PVOID)((ULONG_PTR)MmSystemRangeStart - 1) : (PVOID)MAXULONG_PTR;
Address = MM_ROUND_DOWN(Address, PAGE_SIZE);
if (LowestAddress < MmSystemRangeStart)
{
if (Address >= MmSystemRangeStart)
{
return 0;
}
}
else
{
if (Address < LowestAddress)
{
return 0;
}
}
while (Node != NULL)
{
if (Address < Node->StartingAddress)
{
RightNeighbour = Node;
Node = Node->LeftChild;
}
else if (Address >= Node->EndingAddress)
{
Node = Node->RightChild;
}
else
{
DPRINT("MmFindGapAtAddress: 0\n");
return 0;
}
}
if (RightNeighbour)
{
DPRINT("MmFindGapAtAddress: %p [%p]\n", Address,
(ULONG_PTR)RightNeighbour->StartingAddress - (ULONG_PTR)Address);
return (ULONG_PTR)RightNeighbour->StartingAddress - (ULONG_PTR)Address;
}
else
{
DPRINT("MmFindGapAtAddress: %p [%p]\n", Address,
(ULONG_PTR)HighestAddress - (ULONG_PTR)Address);
return (ULONG_PTR)HighestAddress - (ULONG_PTR)Address;
}
}
VOID
NTAPI
MiRemoveNode(IN PMMADDRESS_NODE Node,
IN PMM_AVL_TABLE Table);
/**
* @name MmFreeMemoryArea
*
* Free an existing memory area.
*
* @param AddressSpace
* Address space to free the area from.
* @param MemoryArea
* Memory area we're about to free.
* @param FreePage
* Callback function for each freed page.
* @param FreePageContext
* Context passed to the callback function.
*
* @return Status
*
* @remarks Lock the address space before calling this function.
*/
NTSTATUS NTAPI
MmFreeMemoryArea(
PMMSUPPORT AddressSpace,
PMEMORY_AREA MemoryArea,
PMM_FREE_PAGE_FUNC FreePage,
PVOID FreePageContext)
{
PMEMORY_AREA *ParentReplace;
ULONG_PTR Address;
PVOID EndAddress;
if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
{
PEPROCESS CurrentProcess = PsGetCurrentProcess();
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
if (Process != NULL &&
Process != CurrentProcess)
{
KeAttachProcess(&Process->Pcb);
}
EndAddress = MM_ROUND_UP(MemoryArea->EndingAddress, PAGE_SIZE);
for (Address = (ULONG_PTR)MemoryArea->StartingAddress;
Address < (ULONG_PTR)EndAddress;
Address += PAGE_SIZE)
{
BOOLEAN Dirty = FALSE;
SWAPENTRY SwapEntry = 0;
PFN_NUMBER Page = 0;
if (MmIsPageSwapEntry(Process, (PVOID)Address))
{
MmDeletePageFileMapping(Process, (PVOID)Address, &SwapEntry);
}
else
{
MmDeleteVirtualMapping(Process, (PVOID)Address, FALSE, &Dirty, &Page);
}
if (FreePage != NULL)
{
FreePage(FreePageContext, MemoryArea, (PVOID)Address,
Page, SwapEntry, (BOOLEAN)Dirty);
}
}
if (Process != NULL &&
Process != CurrentProcess)
{
KeDetachProcess();
}
if (MemoryArea->Vad)
{
ASSERT(MemoryArea->EndingAddress < MmSystemRangeStart);
ASSERT(MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY || MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
/* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
ASSERT(((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare != 0);
if (((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare == 1)
{
MiRemoveNode(MemoryArea->Vad, &Process->VadRoot);
}
ExFreePoolWithTag(MemoryArea->Vad, TAG_MVAD);
MemoryArea->Vad = NULL;
}
}
/* There must be no page ops in progress */
ASSERT(MemoryArea->PageOpCount == 0);
/* Remove the tree item. */
{
if (MemoryArea->Parent != NULL)
{
if (MemoryArea->Parent->LeftChild == MemoryArea)
ParentReplace = &MemoryArea->Parent->LeftChild;
else
ParentReplace = &MemoryArea->Parent->RightChild;
}
else
ParentReplace = (PMEMORY_AREA*)&AddressSpace->WorkingSetExpansionLinks.Flink;
if (MemoryArea->RightChild == NULL)
{
*ParentReplace = MemoryArea->LeftChild;
if (MemoryArea->LeftChild)
MemoryArea->LeftChild->Parent = MemoryArea->Parent;
}
else
{
if (MemoryArea->RightChild->LeftChild == NULL)
{
MemoryArea->RightChild->LeftChild = MemoryArea->LeftChild;
if (MemoryArea->LeftChild)
MemoryArea->LeftChild->Parent = MemoryArea->RightChild;
*ParentReplace = MemoryArea->RightChild;
MemoryArea->RightChild->Parent = MemoryArea->Parent;
}
else
{
PMEMORY_AREA LowestNode;
LowestNode = MemoryArea->RightChild->LeftChild;
while (LowestNode->LeftChild != NULL)
LowestNode = LowestNode->LeftChild;
LowestNode->Parent->LeftChild = LowestNode->RightChild;
if (LowestNode->RightChild)
LowestNode->RightChild->Parent = LowestNode->Parent;
LowestNode->LeftChild = MemoryArea->LeftChild;
if (MemoryArea->LeftChild)
MemoryArea->LeftChild->Parent = LowestNode;
LowestNode->RightChild = MemoryArea->RightChild;
MemoryArea->RightChild->Parent = LowestNode;
*ParentReplace = LowestNode;
LowestNode->Parent = MemoryArea->Parent;
}
}
}
ExFreePoolWithTag(MemoryArea, TAG_MAREA);
DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
return STATUS_SUCCESS;
}
/**
* @name MmCreateMemoryArea
*
* Create a memory area.
*
* @param AddressSpace
* Address space to create the area in.
* @param Type
* Type of the memory area.
* @param BaseAddress
* Base address for the memory area we're about the create. On
* input it contains either 0 (auto-assign address) or preferred
* address. On output it contains the starting address of the
* newly created area.
* @param Length
* Length of the area to allocate.
* @param Attributes
* Protection attributes for the memory area.
* @param Result
* Receives a pointer to the memory area on successful exit.
*
* @return Status
*
* @remarks Lock the address space before calling this function.
*/
NTSTATUS NTAPI
MmCreateMemoryArea(PMMSUPPORT AddressSpace,
ULONG Type,
PVOID *BaseAddress,
ULONG_PTR Length,
ULONG Protect,
PMEMORY_AREA *Result,
BOOLEAN FixedAddress,
ULONG AllocationFlags,
PHYSICAL_ADDRESS BoundaryAddressMultiple)
{
PVOID EndAddress;
ULONG Granularity;
ULONG_PTR tmpLength;
PMEMORY_AREA MemoryArea;
DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %p, "
"*BaseAddress %p, Length %p, AllocationFlags %x, "
"FixedAddress %x, Result %p)\n",
Type, BaseAddress, *BaseAddress, Length, AllocationFlags,
FixedAddress, Result);
Granularity = (MEMORY_AREA_VIRTUAL_MEMORY == Type ? MM_VIRTMEM_GRANULARITY : PAGE_SIZE);
if ((*BaseAddress) == 0 && !FixedAddress)
{
tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, Granularity);
*BaseAddress = MmFindGap(AddressSpace,
tmpLength,
Granularity,
(AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
if ((*BaseAddress) == 0)
{
DPRINT("No suitable gap\n");
return STATUS_NO_MEMORY;
}
}
else
{
tmpLength = Length + ((ULONG_PTR) *BaseAddress
- (ULONG_PTR) MM_ROUND_DOWN(*BaseAddress, Granularity));
tmpLength = (ULONG_PTR)MM_ROUND_UP(tmpLength, Granularity);
*BaseAddress = MM_ROUND_DOWN(*BaseAddress, Granularity);
if (!MmGetAddressSpaceOwner(AddressSpace) && *BaseAddress < MmSystemRangeStart)
{
return STATUS_ACCESS_VIOLATION;
}
if (MmGetAddressSpaceOwner(AddressSpace) &&
(ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart)
{
return STATUS_ACCESS_VIOLATION;
}
if (BoundaryAddressMultiple.QuadPart != 0)
{
EndAddress = ((char*)(*BaseAddress)) + tmpLength-1;
ASSERT(((ULONG_PTR)*BaseAddress/BoundaryAddressMultiple.QuadPart) == ((DWORD_PTR)EndAddress/BoundaryAddressMultiple.QuadPart));
}
if (MmLocateMemoryAreaByRegion(AddressSpace,
*BaseAddress,
tmpLength) != NULL)
{
DPRINT("Memory area already occupied\n");
return STATUS_CONFLICTING_ADDRESSES;
}
}
//
// Is this a static memory area?
//
if (Type & MEMORY_AREA_STATIC)
{
//
// Use the static array instead of the pool
//
ASSERT(MiStaticMemoryAreaCount < MI_STATIC_MEMORY_AREAS);
MemoryArea = &MiStaticMemoryAreas[MiStaticMemoryAreaCount++];
Type &= ~MEMORY_AREA_STATIC;
}
else
{
//
// Allocate the memory area from nonpaged pool
//
MemoryArea = ExAllocatePoolWithTag(NonPagedPool,
sizeof(MEMORY_AREA),
TAG_MAREA);
}
if (!MemoryArea) return STATUS_NO_MEMORY;
RtlZeroMemory(MemoryArea, sizeof(MEMORY_AREA));
MemoryArea->Type = Type;
MemoryArea->StartingAddress = *BaseAddress;
MemoryArea->EndingAddress = (PVOID)((ULONG_PTR)*BaseAddress + tmpLength);
MemoryArea->Protect = Protect;
MemoryArea->Flags = AllocationFlags;
//MemoryArea->LockCount = 0;
MemoryArea->PageOpCount = 0;
MemoryArea->DeleteInProgress = FALSE;
MmInsertMemoryArea(AddressSpace, MemoryArea);
*Result = MemoryArea;
DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
return STATUS_SUCCESS;
}
VOID NTAPI
MmMapMemoryArea(PVOID BaseAddress,
SIZE_T Length,
ULONG Consumer,
ULONG Protection)
{
ULONG i;
NTSTATUS Status;
ASSERT(((ULONG_PTR)BaseAddress % PAGE_SIZE) == 0);
for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
{
PFN_NUMBER Page;
Status = MmRequestPageMemoryConsumer(Consumer, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to allocate page\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
Status = MmCreateVirtualMapping (NULL,
(PVOID)((ULONG_PTR)BaseAddress + (i * PAGE_SIZE)),
Protection,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
}
}
NTSTATUS
NTAPI
MmDeleteProcessAddressSpace(PEPROCESS Process)
{
PVOID Address;
PMEMORY_AREA MemoryArea;
DPRINT("MmDeleteProcessAddressSpace(Process %x (%s))\n", Process,
Process->ImageFileName);
RemoveEntryList(&Process->MmProcessLinks);
MmLockAddressSpace(&Process->Vm);
while ((MemoryArea = (PMEMORY_AREA)Process->Vm.WorkingSetExpansionLinks.Flink) != NULL)
{
switch (MemoryArea->Type)
{
case MEMORY_AREA_SECTION_VIEW:
Address = (PVOID)MemoryArea->StartingAddress;
MmUnlockAddressSpace(&Process->Vm);
MmUnmapViewOfSection(Process, Address);
MmLockAddressSpace(&Process->Vm);
break;
case MEMORY_AREA_VIRTUAL_MEMORY:
MmFreeVirtualMemory(Process, MemoryArea);
break;
case MEMORY_AREA_OWNED_BY_ARM3:
MmFreeMemoryArea(&Process->Vm,
MemoryArea,
NULL,
NULL);
break;
default:
KeBugCheck(MEMORY_MANAGEMENT);
}
}
MmDeleteProcessPageDirectory(Process);
MmUnlockAddressSpace(&Process->Vm);
DPRINT("Finished MmReleaseMmInfo()\n");
return(STATUS_SUCCESS);
}
/* EOF */