663 lines
14 KiB
C++
663 lines
14 KiB
C++
/*++
|
||
|
||
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;
|
||
}
|