reactos/ntoskrnl/mm/ARM3/vadnode.c

880 lines
28 KiB
C
Raw Normal View History

/*
* PROJECT: ReactOS Kernel
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: ntoskrnl/mm/ARM3/vadnode.c
* PURPOSE: ARM Memory Manager VAD Node Algorithms
* PROGRAMMERS: ReactOS Portable Systems Group
* Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#define MODULE_INVOLVED_IN_ARM3
#include <mm/ARM3/miarm.h>
/* Include Mm version of AVL support */
#include "miavl.h"
#include <sdk/lib/rtl/avlsupp.c>
/* GLOBALS ********************************************************************/
CHAR MmReadWrite[32] =
{
MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_NO_ACCESS_ALLOWED, MM_READ_ONLY_ALLOWED, MM_READ_ONLY_ALLOWED,
MM_READ_ONLY_ALLOWED, MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
MM_READ_WRITE_ALLOWED, MM_READ_WRITE_ALLOWED,
};
/* FUNCTIONS ******************************************************************/
PMMVAD
NTAPI
MiLocateAddress(IN PVOID VirtualAddress)
{
PMMVAD FoundVad;
ULONG_PTR Vpn;
PMM_AVL_TABLE Table = &PsGetCurrentProcess()->VadRoot;
TABLE_SEARCH_RESULT SearchResult;
/* Start with the the hint */
FoundVad = (PMMVAD)Table->NodeHint;
if (!FoundVad) return NULL;
/* Check if this VPN is in the hint, if so, use it */
Vpn = (ULONG_PTR)VirtualAddress >> PAGE_SHIFT;
if ((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn)) return FoundVad;
/* VAD hint didn't work, go look for it */
SearchResult = RtlpFindAvlTableNodeOrParent(Table,
(PVOID)Vpn,
(PMMADDRESS_NODE*)&FoundVad);
if (SearchResult != TableFoundNode) return NULL;
/* We found it, update the hint */
ASSERT(FoundVad != NULL);
ASSERT((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn));
Table->NodeHint = FoundVad;
return FoundVad;
}
TABLE_SEARCH_RESULT
NTAPI
MiCheckForConflictingNode(IN ULONG_PTR StartVpn,
IN ULONG_PTR EndVpn,
IN PMM_AVL_TABLE Table,
OUT PMMADDRESS_NODE *NodeOrParent)
{
PMMADDRESS_NODE ParentNode, CurrentNode;
/* If the tree is empty, there is no conflict */
if (Table->NumberGenericTableElements == 0) return TableEmptyTree;
/* Start looping from the root node */
CurrentNode = RtlRightChildAvl(&Table->BalancedRoot);
ASSERT(CurrentNode != NULL);
while (CurrentNode)
{
ParentNode = CurrentNode;
/* This address comes after */
if (StartVpn > CurrentNode->EndingVpn)
{
/* Keep searching on the right */
CurrentNode = RtlRightChildAvl(CurrentNode);
}
else if (EndVpn < CurrentNode->StartingVpn)
{
/* This address ends before the node starts, search on the left */
CurrentNode = RtlLeftChildAvl(CurrentNode);
}
else
{
/* This address is part of this node, return it */
*NodeOrParent = ParentNode;
return TableFoundNode;
}
}
/* There is no more child, save the current node as parent */
*NodeOrParent = ParentNode;
if (StartVpn > ParentNode->EndingVpn)
{
return TableInsertAsRight;
}
else
{
return TableInsertAsLeft;
}
}
VOID
NTAPI
MiInsertNode(IN PMM_AVL_TABLE Table,
IN PMMADDRESS_NODE NewNode,
IN PMMADDRESS_NODE Parent,
IN TABLE_SEARCH_RESULT Result)
{
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
PMMVAD_LONG Vad;
/* Insert it into the tree */
RtlpInsertAvlTreeNode(Table, NewNode, Parent, Result);
/* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
Vad = (PMMVAD_LONG)NewNode;
if (Vad->u.VadFlags.Spare == 0)
{
NTSTATUS Status;
PMEMORY_AREA MemoryArea;
SIZE_T Size;
PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT;
if (AllocatedBase == NULL)
{
AllocatedBase = (PVOID)(ULONG_PTR)1;
Size -= 1;
}
Status = MmCreateMemoryArea(&Process->Vm,
MEMORY_AREA_OWNED_BY_ARM3,
&AllocatedBase,
Size,
PAGE_READWRITE,
&MemoryArea,
0,
PAGE_SIZE);
ASSERT(NT_SUCCESS(Status));
/* Check if this is VM VAD */
if (Vad->ControlArea == NULL)
{
/* We store the reactos MEMORY_AREA here */
Vad->FirstPrototypePte = (PMMPTE)MemoryArea;
}
else
{
/* This is a section VAD. Store the MAREA here for now */
2018-01-01 14:24:05 +00:00
ASSERT(Vad->u4.Banked == (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL);
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
Vad->u4.Banked = (PVOID)MemoryArea;
}
}
}
VOID
NTAPI
MiInsertVad(IN PMMVAD Vad,
IN PMM_AVL_TABLE VadRoot)
{
TABLE_SEARCH_RESULT Result;
PMMADDRESS_NODE Parent = NULL;
/* Validate the VAD and set it as the current hint */
ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
VadRoot->NodeHint = Vad;
/* Find the parent VAD and where this child should be inserted */
Result = RtlpFindAvlTableNodeOrParent(VadRoot, (PVOID)Vad->StartingVpn, &Parent);
ASSERT(Result != TableFoundNode);
ASSERT((Parent != NULL) || (Result == TableEmptyTree));
/* Do the actual insert operation */
MiInsertNode(VadRoot, (PVOID)Vad, Parent, Result);
}
NTSTATUS
NTAPI
MiInsertVadEx(
_Inout_ PMMVAD Vad,
_In_ ULONG_PTR *BaseAddress,
_In_ SIZE_T ViewSize,
_In_ ULONG_PTR HighestAddress,
_In_ ULONG_PTR Alignment,
_In_ ULONG AllocationType)
{
ULONG_PTR StartingAddress, EndingAddress;
PEPROCESS CurrentProcess;
PETHREAD CurrentThread;
TABLE_SEARCH_RESULT Result;
PMMADDRESS_NODE Parent;
/* Align the view size to pages */
ViewSize = ALIGN_UP_BY(ViewSize, PAGE_SIZE);
/* Get the current process */
CurrentProcess = PsGetCurrentProcess();
/* Acquire the address creation lock and make sure the process is alive */
KeAcquireGuardedMutex(&CurrentProcess->AddressCreationLock);
if (CurrentProcess->VmDeleted)
{
KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
DPRINT1("The process is dying\n");
return STATUS_PROCESS_IS_TERMINATING;
}
/* Did the caller specify an address? */
if (*BaseAddress == 0)
{
/* Make sure HighestAddress is not too large */
HighestAddress = min(HighestAddress, (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS);
/* Which way should we search? */
if ((AllocationType & MEM_TOP_DOWN) || CurrentProcess->VmTopDown)
{
/* Find an address top-down */
Result = MiFindEmptyAddressRangeDownTree(ViewSize,
HighestAddress,
Alignment,
&CurrentProcess->VadRoot,
&StartingAddress,
&Parent);
}
else
{
/* Find an address bottom-up */
Result = MiFindEmptyAddressRangeInTree(ViewSize,
Alignment,
&CurrentProcess->VadRoot,
&Parent,
&StartingAddress);
}
/* Get the ending address, which is the last piece we need for the VAD */
EndingAddress = StartingAddress + ViewSize - 1;
/* Check if we found a suitable location */
if ((Result == TableFoundNode) || (EndingAddress > HighestAddress))
{
DPRINT1("Not enough free space to insert this VAD node!\n");
KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
return STATUS_NO_MEMORY;
}
ASSERT(StartingAddress != 0);
ASSERT(StartingAddress < (ULONG_PTR)HighestAddress);
ASSERT(EndingAddress > StartingAddress);
}
else
{
/* Calculate the starting and ending address */
StartingAddress = ALIGN_DOWN_BY(*BaseAddress, Alignment);
EndingAddress = StartingAddress + ViewSize - 1;
/* Make sure it doesn't conflict with an existing allocation */
Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
EndingAddress >> PAGE_SHIFT,
&CurrentProcess->VadRoot,
&Parent);
if (Result == TableFoundNode)
{
DPRINT("Given address conflicts with existing node\n");
KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
return STATUS_CONFLICTING_ADDRESSES;
}
}
/* Now set the VAD address */
Vad->StartingVpn = StartingAddress >> PAGE_SHIFT;
Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
/* Check if we already need to charge for the pages */
if ((Vad->u.VadFlags.PrivateMemory && Vad->u.VadFlags.MemCommit) ||
(!Vad->u.VadFlags.PrivateMemory &&
(Vad->u.VadFlags.Protection & PAGE_WRITECOPY)))
{
/* Set the commit charge */
Vad->u.VadFlags.CommitCharge = ViewSize / PAGE_SIZE;
}
/* Check if the VAD is to be secured */
if (Vad->u2.VadFlags2.OneSecured)
{
/* This *must* be a long VAD! */
ASSERT(Vad->u2.VadFlags2.LongVad);
/* Yeah this is retarded, I didn't invent it! */
((PMMVAD_LONG)Vad)->u3.Secured.StartVpn = StartingAddress;
((PMMVAD_LONG)Vad)->u3.Secured.EndVpn = EndingAddress;
}
/* Lock the working set */
CurrentThread = PsGetCurrentThread();
MiLockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
/* Insert the VAD */
CurrentProcess->VadRoot.NodeHint = Vad;
MiInsertNode(&CurrentProcess->VadRoot, (PVOID)Vad, Parent, Result);
/* Release the working set */
MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
/* Update the process' virtual size, and peak virtual size */
CurrentProcess->VirtualSize += ViewSize;
if (CurrentProcess->VirtualSize > CurrentProcess->PeakVirtualSize)
{
CurrentProcess->PeakVirtualSize = CurrentProcess->VirtualSize;
}
/* Unlock the address space */
KeReleaseGuardedMutex(&CurrentProcess->AddressCreationLock);
*BaseAddress = StartingAddress;
return STATUS_SUCCESS;
}
VOID
NTAPI
MiInsertBasedSection(IN PSECTION Section)
{
TABLE_SEARCH_RESULT Result;
PMMADDRESS_NODE Parent = NULL;
ASSERT(Section->Address.EndingVpn >= Section->Address.StartingVpn);
/* Find the parent VAD and where this child should be inserted */
Result = RtlpFindAvlTableNodeOrParent(&MmSectionBasedRoot, (PVOID)Section->Address.StartingVpn, &Parent);
ASSERT(Result != TableFoundNode);
ASSERT((Parent != NULL) || (Result == TableEmptyTree));
MiInsertNode(&MmSectionBasedRoot, &Section->Address, Parent, Result);
}
VOID
NTAPI
MiRemoveNode(IN PMMADDRESS_NODE Node,
IN PMM_AVL_TABLE Table)
{
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
PMMVAD_LONG Vad;
/* Call the AVL code */
RtlpDeleteAvlTreeNode(Table, Node);
/* Decrease element count */
Table->NumberGenericTableElements--;
/* Check if this node was the hint */
if (Table->NodeHint == Node)
{
/* Get a new hint, unless we're empty now, in which case nothing */
if (!Table->NumberGenericTableElements) Table->NodeHint = NULL;
else Table->NodeHint = Table->BalancedRoot.RightChild;
}
/* Free the node from ReactOS view as well */
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
Vad = (PMMVAD_LONG)Node;
if ((Table != &MmSectionBasedRoot) && (Vad->u.VadFlags.Spare == 0))
{
PMEMORY_AREA MemoryArea;
PEPROCESS Process;
/* Check if this is VM VAD */
if (Vad->ControlArea == NULL)
{
/* We store the ReactOS MEMORY_AREA here */
MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte;
}
else
{
/* This is a section VAD. We store the ReactOS MEMORY_AREA here */
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
MemoryArea = (PMEMORY_AREA)Vad->u4.Banked;
}
/* Make sure one actually still exists */
if (MemoryArea)
{
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
/* Make sure we have not already freed it */
2018-01-01 14:24:05 +00:00
ASSERT(MemoryArea != (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL);
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
/* Get the process */
Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
/* We only create fake memory-areas for ARM3 VADs */
ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
ASSERT(MemoryArea->Vad == NULL);
/* Free it */
MmFreeMemoryArea(&Process->Vm, MemoryArea, NULL, NULL);
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
/* Check if this is VM VAD */
if (Vad->ControlArea == NULL)
{
/* Delete the pointer to it */
2018-01-01 14:24:05 +00:00
Vad->FirstPrototypePte = (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL;
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
}
else
{
/* Delete the pointer to it */
2018-01-01 14:24:05 +00:00
Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBAB1DEADBAB1ULL;
Two Part Patch which fixes ARM3 Section Support (not yet enabled). This had been enabled in the past for testing and resulted in bizare crashes during testing. The amount of fixing required should reveal why: Part 1: Page Fault Path Fixes [NTOS]: As an optimization, someone seems to have had changed the MiResolveDemandZeroFault prototype not to require a PTE, and to instead take a protection mask directly. While clever, this broke support for ARM3 sections, because the code was now assuming that the protection of the PTE for the input address should be used -- while in NT Sections we instead use what are called ProtoType PTEs. This was very annoying to debug, but since the cause has been fixed, I've reverted back to the old convention in which the PTE is passed-in, and this can be a different PTE than the PTE for the address, as it should be. [NTOS]: Due to the reverting of the original path, another optimization, in which MiResolveDemandZeroFault was being called directly instead of going through MiDispatchFault and writing an invalid demand-zero PDE has also been removed. PDE faults are now going through the correct, expected path. [NTOS]: MiResolveDemandZeroFault was always creating Kernel PTEs. It should create User PTEs when necessary. [NTOS]: MiDeletePte was assuming any prototype PTE is a forked PTE. Forked PTEs only happen when the addresses in the PTE don't match, so check for that too. Part 2: ARM3 Section Object Fixes [NTOS]: Fix issue when trying to make both ROS_SECTION_OBJECTs and NT's SECTION co-exist. We relied on the *caller* knowing what kind of section this is, and that can't be a good idea. Now, when the caller requests an ARM3 section vs a ROS section, we use a marker to detect what kind of section this is for later APIs. [NTOS]: For section VADs, we were storing the ReactOS MEMORY_AREA in the ControlArea... however, the mappings of one individual section object share a single control area, even though they have multiple MEMORY_AREAs (one for each mapping). As such, we overwrote the MEMORY_AREA continously, and at free-time, double or triple-freed the same memory area. [NTOS]: Moved the MEMORY_AREA to the "Banked" field of the long VAD, instead of the ControlArea. Allocate MMVAD_LONGs for ARM3 sections for now, to support this. Also, after deleting the MEMORY_AREA while parsing VADs, we now use a special marker to detect double-frees, and we also use a special marker to make sure we have a Long VAD as expected. svn path=/trunk/; revision=56035
2012-03-05 16:41:46 +00:00
}
}
}
}
PMMADDRESS_NODE
NTAPI
MiGetPreviousNode(IN PMMADDRESS_NODE Node)
{
PMMADDRESS_NODE Parent;
/* Get the left child */
if (RtlLeftChildAvl(Node))
{
/* Get right-most child */
Node = RtlLeftChildAvl(Node);
while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
return Node;
}
Parent = RtlParentAvl(Node);
ASSERT(Parent != NULL);
while (Parent != Node)
{
/* The parent should be a right child, return the real predecessor */
if (RtlIsRightChildAvl(Node))
{
/* Return it unless it's the root */
if (Parent == RtlParentAvl(Parent)) Parent = NULL;
return Parent;
}
/* Keep lopping until we find our parent */
Node = Parent;
Parent = RtlParentAvl(Node);
}
/* Nothing found */
return NULL;
}
PMMADDRESS_NODE
NTAPI
MiGetNextNode(IN PMMADDRESS_NODE Node)
{
PMMADDRESS_NODE Parent;
/* Get the right child */
if (RtlRightChildAvl(Node))
{
/* Get left-most child */
Node = RtlRightChildAvl(Node);
while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
return Node;
}
Parent = RtlParentAvl(Node);
ASSERT(Parent != NULL);
while (Parent != Node)
{
/* The parent should be a left child, return the real predecessor */
if (RtlIsLeftChildAvl(Node))
{
/* Return it */
return Parent;
}
/* Keep lopping until we find our parent */
Node = Parent;
Parent = RtlParentAvl(Node);
}
/* Nothing found */
return NULL;
}
TABLE_SEARCH_RESULT
NTAPI
MiFindEmptyAddressRangeInTree(IN SIZE_T Length,
IN ULONG_PTR Alignment,
IN PMM_AVL_TABLE Table,
OUT PMMADDRESS_NODE *PreviousVad,
OUT PULONG_PTR Base)
{
PMMADDRESS_NODE Node, PreviousNode;
ULONG_PTR PageCount, AlignmentVpn, LowVpn, HighestVpn;
ASSERT(Length != 0);
/* Calculate page numbers for the length, alignment, and starting address */
PageCount = BYTES_TO_PAGES(Length);
AlignmentVpn = Alignment >> PAGE_SHIFT;
LowVpn = ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS >> PAGE_SHIFT, AlignmentVpn);
/* Check for kernel mode table (memory areas) */
if (Table->Unused == 1)
{
LowVpn = ALIGN_UP_BY((ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT, AlignmentVpn);
}
/* Check if the table is empty */
if (Table->NumberGenericTableElements == 0)
{
/* Tree is empty, the candidate address is already the best one */
*Base = LowVpn << PAGE_SHIFT;
return TableEmptyTree;
}
/* Otherwise, follow the leftmost child of the right root node's child */
Node = RtlRightChildAvl(&Table->BalancedRoot);
while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
/* Start a search to find a gap */
PreviousNode = NULL;
while (Node != NULL)
{
/* Check if the gap below the current node is suitable */
if (Node->StartingVpn >= LowVpn + PageCount)
{
/* There is enough space to add our node */
*Base = LowVpn << PAGE_SHIFT;
/* Can we use the current node as parent? */
if (RtlLeftChildAvl(Node) == NULL)
{
/* Node has no left child, so use it as parent */
*PreviousVad = Node;
return TableInsertAsLeft;
}
else
{
/* Node has a left child, this means that the previous node is
the right-most child of it's left child and can be used as
the parent. In case we use the space before the left-most
node, it's left child must be NULL. */
ASSERT(PreviousNode != NULL);
ASSERT(RtlRightChildAvl(PreviousNode) == NULL);
*PreviousVad = PreviousNode;
return TableInsertAsRight;
}
}
/* The next candidate is above the current node */
if (Node->EndingVpn >= LowVpn)
LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
/* Remember the current node and go to the next node */
PreviousNode = Node;
Node = MiGetNextNode(Node);
}
/* We're up to the highest VAD, will this allocation fit above it? */
HighestVpn = ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) / PAGE_SIZE;
/* Check for kernel mode table (memory areas) */
if (Table->Unused == 1)
{
HighestVpn = ALIGN_UP_BY((ULONG_PTR)(LONG_PTR)-1 >> PAGE_SHIFT, AlignmentVpn);
}
if (HighestVpn >= LowVpn + PageCount)
{
/* Yes! Use this VAD to store the allocation */
*PreviousVad = PreviousNode;
*Base = LowVpn << PAGE_SHIFT;
return TableInsertAsRight;
}
/* Nyet, there's no free address space for this allocation, so we'll fail */
return TableFoundNode;
}
TABLE_SEARCH_RESULT
NTAPI
MiFindEmptyAddressRangeDownTree(IN SIZE_T Length,
IN ULONG_PTR BoundaryAddress,
IN ULONG_PTR Alignment,
IN PMM_AVL_TABLE Table,
OUT PULONG_PTR Base,
OUT PMMADDRESS_NODE *Parent)
{
PMMADDRESS_NODE Node, OldNode = NULL, Child;
ULONG_PTR LowVpn, HighVpn, AlignmentVpn;
PFN_NUMBER PageCount;
/* Sanity checks */
ASSERT(BoundaryAddress);
ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS));
ASSERT((Alignment & (PAGE_SIZE - 1)) == 0);
/* Calculate page numbers for the length and alignment */
Length = ROUND_TO_PAGES(Length);
PageCount = Length >> PAGE_SHIFT;
AlignmentVpn = Alignment / PAGE_SIZE;
/* Check for kernel mode table (memory areas) */
if (Table->Unused == 1)
{
LowVpn = ALIGN_UP_BY((ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT, AlignmentVpn);
}
else
{
LowVpn = ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS, Alignment);
}
/* Check if there is enough space below the boundary */
if ((LowVpn + Length) > (BoundaryAddress + 1))
{
return TableFoundNode;
}
/* Check if the table is empty */
if (Table->NumberGenericTableElements == 0)
{
/* Tree is empty, the candidate address is already the best one */
*Base = ALIGN_DOWN_BY(BoundaryAddress + 1 - Length, Alignment);
return TableEmptyTree;
}
/* Calculate the initial upper margin */
HighVpn = (BoundaryAddress + 1) >> PAGE_SHIFT;
/* Starting from the root, follow the right children until we found a node
that ends above the boundary */
Node = RtlRightChildAvl(&Table->BalancedRoot);
while ((Node->EndingVpn < HighVpn) &&
((Child = RtlRightChildAvl(Node)) != NULL)) Node = Child;
/* Now loop the Vad nodes */
while (Node)
{
/* Calculate the lower margin */
LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
/* Check if the current bounds are suitable */
if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
{
/* There is enough space to add our node */
LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, AlignmentVpn);
*Base = LowVpn << PAGE_SHIFT;
/* Can we use the current node as parent? */
if (!RtlRightChildAvl(Node))
{
/* Node has no right child, so use it as parent */
*Parent = Node;
return TableInsertAsRight;
}
else
{
/* Node has a right child. This means we must have already
moved one node left from the right-most node we started
with, thus we already have an OldNode! */
ASSERT(OldNode != NULL);
/* The node we had before is the most left grandchild of
that right child, use it as parent. */
ASSERT(RtlLeftChildAvl(OldNode) == NULL);
*Parent = OldNode;
return TableInsertAsLeft;
}
}
/* Update the upper margin if necessary */
if (Node->StartingVpn < HighVpn) HighVpn = Node->StartingVpn;
/* Remember the current node and go to the previous node */
OldNode = Node;
Node = MiGetPreviousNode(Node);
}
/* Check if there's enough space before the lowest Vad */
LowVpn = ALIGN_UP_BY((ULONG_PTR)MI_LOWEST_VAD_ADDRESS, Alignment) / PAGE_SIZE;
if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
{
/* There is enough space to add our address */
LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, Alignment >> PAGE_SHIFT);
*Base = LowVpn << PAGE_SHIFT;
*Parent = OldNode;
return TableInsertAsLeft;
}
/* No address space left at all */
*Base = 0;
*Parent = NULL;
return TableFoundNode;
}
NTSTATUS
NTAPI
MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length,
IN ULONG_PTR BoundaryAddress,
IN ULONG_PTR Alignment,
IN PMM_AVL_TABLE Table,
OUT PULONG_PTR Base)
{
PMMADDRESS_NODE Node, LowestNode;
ULONG_PTR LowVpn, BestVpn;
/* Sanity checks */
ASSERT(Table == &MmSectionBasedRoot);
ASSERT(BoundaryAddress);
ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
/* Compute page length, make sure the boundary address is valid */
Length = ROUND_TO_PAGES(Length);
if ((BoundaryAddress + 1) < Length) return STATUS_NO_MEMORY;
/* Check if the table is empty */
BestVpn = ROUND_DOWN(BoundaryAddress + 1 - Length, Alignment);
if (Table->NumberGenericTableElements == 0)
{
/* Tree is empty, the candidate address is already the best one */
*Base = BestVpn;
return STATUS_SUCCESS;
}
/* Go to the right-most node which should be the biggest address */
Node = Table->BalancedRoot.RightChild;
while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
/* Check if we can fit in here */
LowVpn = ROUND_UP(Node->EndingVpn + 1, Alignment);
if ((LowVpn < BoundaryAddress) && (Length <= (BoundaryAddress - LowVpn)))
{
#if (NTDDI_VERSION >= NTDDI_VISTA)
/* Return the address. */
*Base = BestVpn;
#else
/* Note: this is a compatibility hack that mimics a bug in the 2k3
kernel. It will can waste up to Alignment bytes of memory above
the allocation. This bug was fixed in Windows Vista */
*Base = ROUND_DOWN(BoundaryAddress - Length, Alignment);
#endif
return STATUS_SUCCESS;
}
/* Now loop the Vad nodes */
do
{
/* Break out if we've reached the last node */
LowestNode = MiGetPreviousNode(Node);
if (!LowestNode) break;
/* Check if this node could contain the requested address */
LowVpn = ROUND_UP(LowestNode->EndingVpn + 1, Alignment);
if ((LowestNode->EndingVpn < BestVpn) &&
(LowVpn < Node->StartingVpn) &&
(Length <= (Node->StartingVpn - LowVpn)))
{
/* Check if we need to take BoundaryAddress into account */
if (BoundaryAddress < Node->StartingVpn)
{
/* Return the optimal VPN address */
*Base = BestVpn;
return STATUS_SUCCESS;
}
else
{
/* The upper margin is given by the Node's starting address */
*Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
return STATUS_SUCCESS;
}
}
/* Move to the next node */
Node = LowestNode;
} while (TRUE);
/* Check if there's enough space before the lowest Vad */
if ((Node->StartingVpn > (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) &&
((Node->StartingVpn - (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) >= Length))
{
/* Check if it fits in perfectly */
if (BoundaryAddress < Node->StartingVpn)
{
/* Return the optimal VPN address */
*Base = BestVpn;
return STATUS_SUCCESS;
}
/* Return an aligned base address within this node */
*Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
return STATUS_SUCCESS;
}
/* No address space left at all */
return STATUS_NO_MEMORY;
}
NTSTATUS
NTAPI
MiCheckSecuredVad(IN PMMVAD Vad,
IN PVOID Base,
IN SIZE_T Size,
IN ULONG ProtectionMask)
{
ULONG_PTR StartAddress, EndAddress;
/* Compute start and end address */
StartAddress = (ULONG_PTR)Base;
EndAddress = StartAddress + Size - 1;
/* Are we deleting/unmapping, or changing? */
if (ProtectionMask < MM_DELETE_CHECK)
{
/* Changing... are we allowed to do so? */
if ((Vad->u.VadFlags.NoChange == 1) &&
(Vad->u2.VadFlags2.SecNoChange == 1) &&
(Vad->u.VadFlags.Protection != ProtectionMask))
{
/* Nope, bail out */
DPRINT1("Trying to mess with a no-change VAD!\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
}
else
{
/* This is allowed */
ProtectionMask = 0;
}
/* ARM3 doesn't support this yet */
ASSERT(Vad->u2.VadFlags2.MultipleSecured == 0);
/* Is this a one-secured VAD, like a TEB or PEB? */
if (Vad->u2.VadFlags2.OneSecured)
{
/* Is this allocation being described by the VAD? */
if ((StartAddress <= ((PMMVAD_LONG)Vad)->u3.Secured.EndVpn) &&
(EndAddress >= ((PMMVAD_LONG)Vad)->u3.Secured.StartVpn))
{
/* Guard page? */
if (ProtectionMask & MM_DECOMMIT)
{
DPRINT1("Not allowed to change protection on guard page!\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
/* ARM3 doesn't have read-only VADs yet */
ASSERT(Vad->u2.VadFlags2.ReadOnly == 0);
/* Check if read-write protections are allowed */
if (MmReadWrite[ProtectionMask] < MM_READ_WRITE_ALLOWED)
{
DPRINT1("Invalid protection mask for RW access!\n");
return STATUS_INVALID_PAGE_PROTECTION;
}
}
}
/* All good, allow the change */
return STATUS_SUCCESS;
}
/* EOF */