Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

662 lines
14 KiB

/*++
Copyright (c) 1999-2001 Microsoft Corporation
Module Name:
mmap.cpp
Abstract:
Implementation of memory map class.
Author:
Matthew D Hendel (math) 16-Sep-1999
Revision History:
--*/
#include "ntsdp.hpp"
#define MEMORY_MAP_CHECK (0xDEADF00D)
typedef struct _MEMORY_MAP_ENTRY
{
ULONG64 BaseOfRegion;
ULONG SizeOfRegion;
PVOID Region;
PVOID UserData;
BOOL AllowOverlap;
struct _MEMORY_MAP_ENTRY * Next;
} MEMORY_MAP_ENTRY, * PMEMORY_MAP_ENTRY;
typedef struct _MEMORY_MAP
{
PMEMORY_MAP_ENTRY List;
ULONG Check;
ULONG RegionCount;
} MEMORY_MAP, * PMEMORY_MAP;
PMEMORY_MAP MemoryMap;
PMEMORY_MAP_ENTRY
MemoryMap_FindPreceedingRegion(
ULONG64 Addr
);
PMEMORY_MAP_ENTRY
MemoryMap_FindContainingRegion(
ULONG64 Addr
);
BOOL
MemoryMap_Create(
VOID
)
/*++
--*/
{
MemoryMap = (PMEMORY_MAP) malloc ( sizeof (MEMORY_MAP) );
if (!MemoryMap)
{
return FALSE;
}
MemoryMap->Check = MEMORY_MAP_CHECK;
MemoryMap->RegionCount = 0;
MemoryMap->List = NULL;
return TRUE;
}
BOOL
MemoryMap_Destroy(
VOID
)
/*++
--*/
{
if (MemoryMap == NULL)
{
return TRUE;
}
PMEMORY_MAP_ENTRY Entry;
PMEMORY_MAP_ENTRY Next;
Entry = MemoryMap->List;
while ( Entry != NULL )
{
Next = Entry->Next;
free ( Entry );
Entry = Next;
}
MemoryMap->Check = 0;
free ( MemoryMap );
MemoryMap = NULL;
return TRUE;
}
PMEMORY_MAP_ENTRY
AddMapEntry(ULONG64 BaseOfRegion, ULONG SizeOfRegion,
PVOID Buffer, PVOID UserData,
BOOL AllowOverlap)
{
PMEMORY_MAP_ENTRY PrevEntry;
PMEMORY_MAP_ENTRY MapEntry;
MapEntry = (MEMORY_MAP_ENTRY *)malloc ( sizeof ( MEMORY_MAP_ENTRY ) );
if (!MapEntry)
{
return NULL;
}
MapEntry->BaseOfRegion = BaseOfRegion;
MapEntry->SizeOfRegion = SizeOfRegion;
MapEntry->Region = Buffer;
MapEntry->UserData = UserData;
MapEntry->AllowOverlap = AllowOverlap;
MapEntry->Next = NULL;
//
// Insert the element.
//
PrevEntry = MemoryMap_FindPreceedingRegion(BaseOfRegion);
if ( PrevEntry == NULL )
{
//
// Insert at head.
//
MapEntry->Next = MemoryMap->List;
MemoryMap->List = MapEntry;
}
else
{
//
// Insert in order.
//
MapEntry->Next = PrevEntry->Next;
PrevEntry->Next = MapEntry;
}
MemoryMap->RegionCount++;
return MapEntry;
}
HRESULT
MemoryMap_AddRegion(
IN ULONG64 BaseOfRegion,
IN ULONG SizeOfRegion,
IN PVOID Buffer,
IN PVOID UserData,
IN BOOL AllowOverlap
)
/*++
Routine Description:
Add a region to a memory map. The region must not overlap with any
other region that is already contained in the map.
Return Values:
TRUE - On success.
FALSE - On failure.
--*/
{
PMEMORY_MAP_ENTRY PrevEntry;
PMEMORY_MAP_ENTRY MapEntry;
//
// The region size cannot be zero.
//
if (SizeOfRegion == 0)
{
ErrOut("**** MemoryMap_AddRegion: Empty region being added.\n");
return S_OK;
}
if (IsBadReadPtr(Buffer, SizeOfRegion))
{
ErrOut("**** MemoryMap_AddRegion: Mapping too small to map "
"%s:%X from %p\n", FormatAddr64(BaseOfRegion),
SizeOfRegion, Buffer);
return E_INVALIDARG;
}
if (MemoryMap == NULL)
{
ErrOut("Memory map not initialized\n");
return E_NOINTERFACE;
}
ULONG64 EndOfRegion;
ULONG64 EndOfEntryRegion;
PMEMORY_MAP_ENTRY Entry;
ULONG Size;
while (SizeOfRegion > 0)
{
//
// Find the first overlapping region.
// We need to rescan the whole list as it
// may have changed due to insertion of fragments
// of the new region.
//
EndOfRegion = BaseOfRegion + SizeOfRegion;
for (Entry = MemoryMap->List; Entry != NULL; Entry = Entry->Next)
{
EndOfEntryRegion = Entry->BaseOfRegion + Entry->SizeOfRegion;
if (EndOfRegion > Entry->BaseOfRegion &&
Entry->BaseOfRegion >= BaseOfRegion)
{
if (AllowOverlap || Entry->AllowOverlap)
{
// Overlap can occur when a stack, ethread or
// eprocess is taken from static data in an image.
// For example, the x86 idle process is static
// data in ntoskrnl. Triage dumps contain the
// eprocess data and it gets mapped and can overlap
// with the ntoskrnl image.
#if 0
WarnOut("WARNING: Allowing overlapped region %s - %s\n",
FormatAddr64(BaseOfRegion),
FormatAddr64(BaseOfRegion + SizeOfRegion - 1));
#endif
Entry = NULL;
}
break;
}
}
if (Entry == NULL ||
BaseOfRegion < Entry->BaseOfRegion)
{
// There's a portion of the beginning of the new
// region which does not overlap so add it and
// trim the description.
Size = Entry == NULL ? SizeOfRegion :
(ULONG)(Entry->BaseOfRegion - BaseOfRegion);
if (!AddMapEntry(BaseOfRegion, Size, Buffer, UserData,
AllowOverlap))
{
return E_OUTOFMEMORY;
}
if (Size == SizeOfRegion)
{
// None of the region overlapped so we're done.
return S_OK;
}
BaseOfRegion += Size;
SizeOfRegion -= Size;
Buffer = (PUCHAR)Buffer + Size;
}
//
// Now handle the overlap.
//
if (SizeOfRegion > Entry->SizeOfRegion)
{
Size = Entry->SizeOfRegion;
}
else
{
Size = SizeOfRegion;
}
if (memcmp(Buffer, Entry->Region, Size))
{
ErrOut("**** MemoryMap_AddRegion: "
"Conflicting region %s - %s\n",
FormatAddr64(BaseOfRegion),
FormatAddr64(BaseOfRegion + SizeOfRegion - 1));
return HR_REGION_CONFLICT;
}
// Overlap region data matched so it's OK to just
// trim the overlap out of the new region and move
// on to the next possible overlap.
BaseOfRegion += Size;
SizeOfRegion -= Size;
Buffer = (PUCHAR)Buffer + Size;
}
return S_OK;
}
BOOL
MemoryMap_ReadMemory(
IN ULONG64 BaseOfRange,
IN OUT PVOID Buffer,
IN ULONG SizeOfRange,
OUT ULONG * BytesRead
)
/*++
Routine Description:
Read memory from the memory map. ReadMemory can read across regions, as
long as there is no unallocated space between regions.
This routine will do a partial read if the region ends before
SizeOfRange bytes have been read. In that case BytesRead will return the
number of bytes actually read.
Arguments:
BaseOfRange - The base address where we want to read memory from.
SizeOfRange - The length of the region to read memory from.
Buffer - A pointer to a buffer to read memory into.
BytesRead - On success, the number of bytes successfully read.
Return Values:
TRUE - If any number of bytes were successfully read from the memory map.
FALSE - If no bytes were read.
--*/
{
ULONG BytesToReadFromRegion;
PMEMORY_MAP_ENTRY Entry;
ULONG64 BaseToRead;
ULONG SizeToRead;
PBYTE BufferForRead;
ULONG_PTR OffsetToRead;
ULONG Read;
ULONG AvailSize;
//
// We return TRUE if we read any bytes successfully and FALSE if not.
//
*BytesRead = 0;
BaseToRead = BaseOfRange;
SizeToRead = SizeOfRange;
BufferForRead = (PBYTE) Buffer;
do
{
Entry = MemoryMap_FindContainingRegion(BaseToRead);
if ( !Entry )
{
if (*BytesRead)
{
return TRUE;
}
else
{
return FALSE;
}
}
PMEMORY_MAP_ENTRY NextEntry = Entry->Next;
// Due to overlap there may be other entries
// that need to be processed even before the
// end of the containing region.
AvailSize = Entry->SizeOfRegion;
while (NextEntry != NULL)
{
if (NextEntry->BaseOfRegion > BaseToRead)
{
ULONG64 EntryDiff =
NextEntry->BaseOfRegion - Entry->BaseOfRegion;
if (EntryDiff < AvailSize)
{
AvailSize = (ULONG)EntryDiff;
}
break;
}
NextEntry = NextEntry->Next;
}
if (BaseToRead + SizeToRead > Entry->BaseOfRegion + AvailSize)
{
BytesToReadFromRegion = (ULONG)
(Entry->BaseOfRegion - BaseToRead) + AvailSize;
}
else
{
BytesToReadFromRegion = SizeToRead;
}
OffsetToRead = (ULONG_PTR) (BaseToRead - Entry->BaseOfRegion);
__try
{
RtlCopyMemory (BufferForRead,
(PBYTE)Entry->Region + OffsetToRead,
BytesToReadFromRegion
);
}
__except(MappingExceptionFilter(GetExceptionInformation()))
{
return FALSE;
}
*BytesRead += BytesToReadFromRegion;
BaseToRead += BytesToReadFromRegion;
SizeToRead -= BytesToReadFromRegion;
BufferForRead += BytesToReadFromRegion;
} while ( SizeToRead );
return TRUE;
}
BOOL
MemoryMap_GetRegionInfo(
IN ULONG64 Addr,
OUT ULONG64* BaseOfRegion, OPTIONAL
OUT ULONG* SizeOfRegion, OPTIONAL
OUT PVOID* Buffer, OPTIONAL
OUT PVOID* UserData OPTIONAL
)
/*++
Routine Description:
Get information about the region containing the address Addr.
Arguments:
Addr - An address that is contained within some region in the map.
BaseOfRegion - Pointer to a buffer to return the region base.
SizeOfRegion - Pointer to a buffer to retutn the region size.
Buffer - Pointer to a buffer to return the region buffer pointer.
UserData - Pointer to a buffer to return the region client param.
Return Values:
TRUE - On success.
FALSE - On failure.
--*/
{
PMEMORY_MAP_ENTRY Entry;
Entry = MemoryMap_FindContainingRegion(Addr);
if ( !Entry )
{
return FALSE;
}
if ( BaseOfRegion )
{
*BaseOfRegion = Entry->BaseOfRegion;
}
if ( SizeOfRegion )
{
*SizeOfRegion = Entry->SizeOfRegion;
}
if ( Buffer )
{
*Buffer = Entry->Region;
}
if ( UserData )
{
*UserData = Entry->UserData;
}
return TRUE;
}
BOOL
MemoryMap_RemoveRegion(
IN ULONG64 BaseOfRegion,
IN ULONG SizeOfRegion
)
{
if (MemoryMap == NULL)
{
return FALSE;
}
// XXX drewb - This should carve the given region out of
// any existing regions. Right now we don't need general
// removal functionality though so only handle the case
// where the requested removal is an exact single region
// match.
PMEMORY_MAP_ENTRY PrevEntry;
PMEMORY_MAP_ENTRY Entry;
PrevEntry = MemoryMap_FindPreceedingRegion(BaseOfRegion);
if (PrevEntry != NULL)
{
Entry = PrevEntry->Next;
}
else
{
Entry = MemoryMap->List;
}
if (Entry == NULL)
{
ErrOut("MemoryMap_RemoveRegion NULL region for %s:%x\n",
FormatAddr64(BaseOfRegion), SizeOfRegion);
return FALSE;
}
else if (Entry->BaseOfRegion != BaseOfRegion ||
Entry->SizeOfRegion != SizeOfRegion)
{
ErrOut("MemoryMap_RemoveRegion region mismatch: "
"%s:%x vs. entry %s:%x\n",
FormatAddr64(BaseOfRegion), SizeOfRegion,
FormatAddr64(Entry->BaseOfRegion), Entry->SizeOfRegion);
return FALSE;
}
if (PrevEntry == NULL)
{
MemoryMap->List = Entry->Next;
}
else
{
PrevEntry->Next = Entry->Next;
}
free(Entry);
MemoryMap->RegionCount--;
return TRUE;
}
//
// Private functions
//
PMEMORY_MAP_ENTRY
MemoryMap_FindPreceedingRegion(
IN ULONG64 BaseOfRegion
)
{
if (MemoryMap == NULL)
{
return NULL;
}
PMEMORY_MAP_ENTRY PrevEntry;
PMEMORY_MAP_ENTRY Entry;
PrevEntry = NULL;
Entry = MemoryMap->List;
while (Entry != NULL)
{
//
// Assuming they're in order.
//
if ( Entry->BaseOfRegion >= BaseOfRegion )
{
return PrevEntry;
}
PrevEntry = Entry;
Entry = Entry->Next;
}
return PrevEntry;
}
PMEMORY_MAP_ENTRY
MemoryMap_FindContainingRegion(
IN ULONG64 Addr
)
{
if (MemoryMap == NULL)
{
return NULL;
}
PMEMORY_MAP_ENTRY Entry;
PMEMORY_MAP_ENTRY ReturnEntry = NULL;
Entry = MemoryMap->List;
//
// We may have overlapping regions, so keep going until we find
// the most precise one (assumed to be the one we care about)
//
while ( Entry != NULL )
{
if ( Entry->BaseOfRegion <= Addr &&
Addr < Entry->BaseOfRegion + Entry->SizeOfRegion)
{
ReturnEntry = Entry;
}
else if (ReturnEntry != NULL &&
Addr >= ReturnEntry->BaseOfRegion + ReturnEntry->SizeOfRegion)
{
// Optimization - we can stop searching if we've already
// found a block and have now left its region. We can't
// stop as long as we're in its region as there may
// be more exact overlaps anywhere within the entire region.
break;
}
Entry = Entry->Next;
}
return ReturnEntry;
}