mirror of
https://github.com/reactos/reactos.git
synced 2024-12-05 01:13:31 +08:00
286 lines
9.3 KiB
C
286 lines
9.3 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/region.c
|
|
* PURPOSE: No purpose listed.
|
|
*
|
|
* PROGRAMMERS: David Welch
|
|
*/
|
|
|
|
/* INCLUDE *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static VOID
|
|
InsertAfterEntry(PLIST_ENTRY Previous,
|
|
PLIST_ENTRY Entry)
|
|
/*
|
|
* FUNCTION: Insert a list entry after another entry in the list
|
|
*/
|
|
{
|
|
Previous->Flink->Blink = Entry;
|
|
|
|
Entry->Flink = Previous->Flink;
|
|
Entry->Blink = Previous;
|
|
|
|
Previous->Flink = Entry;
|
|
}
|
|
|
|
static PMM_REGION
|
|
MmSplitRegion(PMM_REGION InitialRegion, PVOID InitialBaseAddress,
|
|
PVOID StartAddress, SIZE_T Length, ULONG NewType,
|
|
ULONG NewProtect, PMMSUPPORT AddressSpace,
|
|
PMM_ALTER_REGION_FUNC AlterFunc)
|
|
{
|
|
PMM_REGION NewRegion1;
|
|
PMM_REGION NewRegion2;
|
|
SIZE_T InternalLength;
|
|
|
|
/* Allocate this in front otherwise the failure case is too difficult. */
|
|
NewRegion2 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
|
|
TAG_MM_REGION);
|
|
if (NewRegion2 == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
/* Create the new region. */
|
|
NewRegion1 = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
|
|
TAG_MM_REGION);
|
|
if (NewRegion1 == NULL)
|
|
{
|
|
ExFreePoolWithTag(NewRegion2, TAG_MM_REGION);
|
|
return(NULL);
|
|
}
|
|
NewRegion1->Type = NewType;
|
|
NewRegion1->Protect = NewProtect;
|
|
InternalLength = ((char*)InitialBaseAddress + InitialRegion->Length) - (char*)StartAddress;
|
|
InternalLength = min(InternalLength, Length);
|
|
NewRegion1->Length = InternalLength;
|
|
InsertAfterEntry(&InitialRegion->RegionListEntry,
|
|
&NewRegion1->RegionListEntry);
|
|
|
|
/*
|
|
* Call our helper function to do the changes on the addresses contained
|
|
* in the initial region.
|
|
*/
|
|
AlterFunc(AddressSpace, StartAddress, InternalLength, InitialRegion->Type,
|
|
InitialRegion->Protect, NewType, NewProtect);
|
|
|
|
/*
|
|
* If necessary create a new region for the portion of the initial region
|
|
* beyond the range of addresses to alter.
|
|
*/
|
|
if (((char*)InitialBaseAddress + InitialRegion->Length) > ((char*)StartAddress + Length))
|
|
{
|
|
NewRegion2->Type = InitialRegion->Type;
|
|
NewRegion2->Protect = InitialRegion->Protect;
|
|
NewRegion2->Length = ((char*)InitialBaseAddress + InitialRegion->Length) -
|
|
((char*)StartAddress + Length);
|
|
InsertAfterEntry(&NewRegion1->RegionListEntry,
|
|
&NewRegion2->RegionListEntry);
|
|
}
|
|
else
|
|
{
|
|
ExFreePoolWithTag(NewRegion2, TAG_MM_REGION);
|
|
}
|
|
|
|
/* Either remove or shrink the initial region. */
|
|
if (InitialBaseAddress == StartAddress)
|
|
{
|
|
RemoveEntryList(&InitialRegion->RegionListEntry);
|
|
ExFreePoolWithTag(InitialRegion, TAG_MM_REGION);
|
|
}
|
|
else
|
|
{
|
|
InitialRegion->Length = (char*)StartAddress - (char*)InitialBaseAddress;
|
|
}
|
|
|
|
return(NewRegion1);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmAlterRegion(PMMSUPPORT AddressSpace, PVOID BaseAddress,
|
|
PLIST_ENTRY RegionListHead, PVOID StartAddress, SIZE_T Length,
|
|
ULONG NewType, ULONG NewProtect, PMM_ALTER_REGION_FUNC AlterFunc)
|
|
{
|
|
PMM_REGION InitialRegion;
|
|
PVOID InitialBaseAddress = NULL;
|
|
PMM_REGION NewRegion;
|
|
PLIST_ENTRY CurrentEntry;
|
|
PMM_REGION CurrentRegion = NULL;
|
|
PVOID CurrentBaseAddress;
|
|
SIZE_T RemainingLength;
|
|
|
|
/*
|
|
* Find the first region containing part of the range of addresses to
|
|
* be altered.
|
|
*/
|
|
InitialRegion = MmFindRegion(BaseAddress, RegionListHead, StartAddress,
|
|
&InitialBaseAddress);
|
|
/*
|
|
* If necessary then split the region into the affected and unaffected parts.
|
|
*/
|
|
if (InitialRegion->Type != NewType || InitialRegion->Protect != NewProtect)
|
|
{
|
|
NewRegion = MmSplitRegion(InitialRegion, InitialBaseAddress,
|
|
StartAddress, Length, NewType, NewProtect,
|
|
AddressSpace, AlterFunc);
|
|
if (NewRegion == NULL)
|
|
{
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
if(NewRegion->Length < Length)
|
|
RemainingLength = Length - NewRegion->Length;
|
|
else
|
|
RemainingLength = 0;
|
|
}
|
|
else
|
|
{
|
|
NewRegion = InitialRegion;
|
|
if(((ULONG_PTR)InitialBaseAddress + NewRegion->Length) <
|
|
((ULONG_PTR)StartAddress + Length))
|
|
RemainingLength = ((ULONG_PTR)StartAddress + Length) - ((ULONG_PTR)InitialBaseAddress + NewRegion->Length);
|
|
else
|
|
RemainingLength = 0;
|
|
}
|
|
|
|
/*
|
|
* Free any complete regions that are containing in the range of addresses
|
|
* and call the helper function to actually do the changes.
|
|
*/
|
|
CurrentEntry = NewRegion->RegionListEntry.Flink;
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
|
|
RegionListEntry);
|
|
CurrentBaseAddress = (char*)StartAddress + NewRegion->Length;
|
|
while (RemainingLength > 0 && CurrentRegion->Length <= RemainingLength &&
|
|
CurrentEntry != RegionListHead)
|
|
{
|
|
if (CurrentRegion->Type != NewType ||
|
|
CurrentRegion->Protect != NewProtect)
|
|
{
|
|
AlterFunc(AddressSpace, CurrentBaseAddress, CurrentRegion->Length,
|
|
CurrentRegion->Type, CurrentRegion->Protect,
|
|
NewType, NewProtect);
|
|
}
|
|
|
|
CurrentBaseAddress = (PVOID)((ULONG_PTR)CurrentBaseAddress + CurrentRegion->Length);
|
|
NewRegion->Length += CurrentRegion->Length;
|
|
RemainingLength -= CurrentRegion->Length;
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
RemoveEntryList(&CurrentRegion->RegionListEntry);
|
|
ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
|
|
RegionListEntry);
|
|
}
|
|
|
|
/*
|
|
* Split any final region.
|
|
*/
|
|
if (RemainingLength > 0 && CurrentEntry != RegionListHead)
|
|
{
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
|
|
RegionListEntry);
|
|
if (CurrentRegion->Type != NewType ||
|
|
CurrentRegion->Protect != NewProtect)
|
|
{
|
|
AlterFunc(AddressSpace, CurrentBaseAddress, RemainingLength,
|
|
CurrentRegion->Type, CurrentRegion->Protect,
|
|
NewType, NewProtect);
|
|
}
|
|
NewRegion->Length += RemainingLength;
|
|
CurrentRegion->Length -= RemainingLength;
|
|
}
|
|
|
|
/*
|
|
* If the region after the new region has the same type then merge them.
|
|
*/
|
|
if (NewRegion->RegionListEntry.Flink != RegionListHead)
|
|
{
|
|
CurrentEntry = NewRegion->RegionListEntry.Flink;
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
|
|
RegionListEntry);
|
|
if (CurrentRegion->Type == NewRegion->Type &&
|
|
CurrentRegion->Protect == NewRegion->Protect)
|
|
{
|
|
NewRegion->Length += CurrentRegion->Length;
|
|
RemoveEntryList(&CurrentRegion->RegionListEntry);
|
|
ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the region before the new region has the same type then merge them.
|
|
*/
|
|
if (NewRegion->RegionListEntry.Blink != RegionListHead)
|
|
{
|
|
CurrentEntry = NewRegion->RegionListEntry.Blink;
|
|
CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION,
|
|
RegionListEntry);
|
|
if (CurrentRegion->Type == NewRegion->Type &&
|
|
CurrentRegion->Protect == NewRegion->Protect)
|
|
{
|
|
NewRegion->Length += CurrentRegion->Length;
|
|
RemoveEntryList(&CurrentRegion->RegionListEntry);
|
|
ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
|
|
}
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MmInitializeRegion(PLIST_ENTRY RegionListHead, SIZE_T Length, ULONG Type,
|
|
ULONG Protect)
|
|
{
|
|
PMM_REGION Region;
|
|
|
|
Region = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_REGION),
|
|
TAG_MM_REGION);
|
|
if (!Region) return;
|
|
|
|
Region->Type = Type;
|
|
Region->Protect = Protect;
|
|
Region->Length = Length;
|
|
InitializeListHead(RegionListHead);
|
|
InsertHeadList(RegionListHead, &Region->RegionListEntry);
|
|
}
|
|
|
|
PMM_REGION
|
|
NTAPI
|
|
MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address,
|
|
PVOID* RegionBaseAddress)
|
|
{
|
|
PLIST_ENTRY current_entry;
|
|
PMM_REGION current;
|
|
PVOID StartAddress = BaseAddress;
|
|
|
|
current_entry = RegionListHead->Flink;
|
|
while (current_entry != RegionListHead)
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
|
|
|
|
if (StartAddress <= Address &&
|
|
((char*)StartAddress + current->Length) > (char*)Address)
|
|
{
|
|
if (RegionBaseAddress != NULL)
|
|
{
|
|
*RegionBaseAddress = StartAddress;
|
|
}
|
|
return(current);
|
|
}
|
|
|
|
current_entry = current_entry->Flink;
|
|
|
|
StartAddress = (PVOID)((ULONG_PTR)StartAddress + current->Length);
|
|
|
|
}
|
|
return(NULL);
|
|
}
|