810 lines
18 KiB
C
810 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
range.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the range list routines for the Plug and Play Memory
|
|
driver.
|
|
|
|
Author:
|
|
|
|
Dave Richards (daveri) 16-Aug-1999
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pnpmem.h"
|
|
|
|
PPM_RANGE_LIST_ENTRY
|
|
PmAllocateRangeListEntry(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PmFreeRangeListEntry(
|
|
IN PPM_RANGE_LIST_ENTRY RangeListEntry
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PmAllocateRangeListEntry)
|
|
#pragma alloc_text(PAGE, PmInsertRangeInList)
|
|
#pragma alloc_text(PAGE, PmFreeRangeListEntry)
|
|
#pragma alloc_text(PAGE, PmAllocateRangeList)
|
|
#pragma alloc_text(PAGE, PmFreeRangeList)
|
|
#pragma alloc_text(PAGE, PmIsRangeListEmpty)
|
|
#pragma alloc_text(PAGE, PmDebugDumpRangeList)
|
|
#pragma alloc_text(PAGE, PmCopyRangeList)
|
|
#pragma alloc_text(PAGE, PmSubtractRangeList)
|
|
#pragma alloc_text(PAGE, PmIntersectRangeList)
|
|
#pragma alloc_text(PAGE, PmCreateRangeListFromCmResourceList)
|
|
#pragma alloc_text(PAGE, PmCreateRangeListFromPhysicalMemoryRanges)
|
|
#endif
|
|
|
|
|
|
PPM_RANGE_LIST_ENTRY
|
|
PmAllocateRangeListEntry(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates a range list entry from paged pool.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to a PM_RANGE_LIST_ENTRY object is returned,
|
|
otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
RangeListEntry = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof (PM_RANGE_LIST_ENTRY)
|
|
);
|
|
|
|
return RangeListEntry;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmInsertRangeInList(
|
|
PPM_RANGE_LIST InsertionList,
|
|
ULONGLONG Start,
|
|
ULONGLONG End
|
|
)
|
|
{
|
|
PPM_RANGE_LIST_ENTRY entry;
|
|
|
|
entry = PmAllocateRangeListEntry();
|
|
if (entry == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
entry->Start = Start;
|
|
entry->End = End;
|
|
|
|
InsertTailList(&InsertionList->List,
|
|
&entry->ListEntry);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PmFreeRangeListEntry(
|
|
IN PPM_RANGE_LIST_ENTRY RangeListEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function de-allocates a range list entry object.
|
|
|
|
Arguments:
|
|
|
|
RangeListEntry - The PM_RANGE_LIST_ENTRY to be de-allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(RangeListEntry != NULL);
|
|
|
|
ExFreePool(RangeListEntry);
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmAllocateRangeList(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates and initializes a range list from paged pool.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to a PM_RANGE_LIST object is returned, otherwise
|
|
NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST RangeList;
|
|
|
|
PAGED_CODE();
|
|
|
|
RangeList = ExAllocatePool(
|
|
PagedPool,
|
|
sizeof (PM_RANGE_LIST)
|
|
);
|
|
|
|
if (RangeList != NULL) {
|
|
InitializeListHead(&RangeList->List);
|
|
}
|
|
|
|
return RangeList;
|
|
}
|
|
|
|
VOID
|
|
PmFreeRangeList(
|
|
IN PPM_RANGE_LIST RangeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function removes all entries from a range list and de-allocates it.
|
|
|
|
Arguments:
|
|
|
|
RangeList - The PM_RANGE_LIST to be de-allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
for (ListEntry = RangeList->List.Flink;
|
|
ListEntry != &RangeList->List;
|
|
ListEntry = RangeList->List.Flink) {
|
|
|
|
RangeListEntry = CONTAINING_RECORD(
|
|
ListEntry,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
RemoveEntryList(ListEntry);
|
|
|
|
PmFreeRangeListEntry(RangeListEntry);
|
|
|
|
}
|
|
|
|
ExFreePool(RangeList);
|
|
}
|
|
|
|
BOOLEAN
|
|
PmIsRangeListEmpty(
|
|
IN PPM_RANGE_LIST RangeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines whether the specified range list is empty.
|
|
|
|
Arguments:
|
|
|
|
RangeList - The PM_RANGE_LIST.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the PM_RANGE_LIST has no PM_RANGE_LIST_ENTRYs, otherwise FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return IsListEmpty(&RangeList->List);
|
|
}
|
|
|
|
VOID
|
|
PmDebugDumpRangeList(
|
|
IN ULONG DebugPrintLevel,
|
|
IN PCCHAR DebugMessage,
|
|
PPM_RANGE_LIST RangeList
|
|
)
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
PPM_RANGE_LIST_ENTRY rangeListEntry;
|
|
|
|
PmPrint((DebugPrintLevel, DebugMessage));
|
|
|
|
if (RangeList == NULL) {
|
|
PmPrint((DebugPrintLevel, "\tNULL\n"));
|
|
return;
|
|
} else if (PmIsRangeListEmpty(RangeList)) {
|
|
PmPrint((DebugPrintLevel, "\tEmpty\n"));
|
|
return;
|
|
} else {
|
|
for (listEntry = RangeList->List.Flink;
|
|
listEntry != &RangeList->List;
|
|
listEntry = listEntry->Flink) {
|
|
|
|
rangeListEntry = CONTAINING_RECORD(
|
|
listEntry,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
PmPrint((DebugPrintLevel, "\t0x%I64X - 0x%I64X\n",
|
|
rangeListEntry->Start, rangeListEntry->End));
|
|
}
|
|
}
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmCopyRangeList(
|
|
IN PPM_RANGE_LIST SrcRangeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a copy of a PM_RANGE_LIST and its supporting
|
|
PM_RANGE_LIST_ENTRY objects.
|
|
|
|
Arguments:
|
|
|
|
SrcRangeList - The PM_RANGE_LIST to be copied.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to a PM_RANGE_LIST is returned, otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST DstRangeList;
|
|
PLIST_ENTRY ListEntry;
|
|
PPM_RANGE_LIST_ENTRY SrcRangeListEntry;
|
|
PPM_RANGE_LIST_ENTRY DstRangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
DstRangeList = PmAllocateRangeList();
|
|
|
|
if (DstRangeList != NULL) {
|
|
|
|
for (ListEntry = SrcRangeList->List.Flink;
|
|
ListEntry != &SrcRangeList->List;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
SrcRangeListEntry = CONTAINING_RECORD(
|
|
ListEntry,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
DstRangeListEntry = PmAllocateRangeListEntry();
|
|
|
|
if (DstRangeListEntry == NULL) {
|
|
PmFreeRangeList(DstRangeList);
|
|
DstRangeList = NULL;
|
|
break;
|
|
}
|
|
|
|
DstRangeListEntry->Start = SrcRangeListEntry->Start;
|
|
DstRangeListEntry->End = SrcRangeListEntry->End;
|
|
|
|
InsertTailList(
|
|
&DstRangeList->List,
|
|
&DstRangeListEntry->ListEntry
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return DstRangeList;
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmSubtractRangeList(
|
|
IN PPM_RANGE_LIST MinuendList,
|
|
IN PPM_RANGE_LIST SubtrahendList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a new range list which represents the set difference
|
|
between MinuendList and SubtrahendList.
|
|
|
|
Arguments:
|
|
|
|
MinuendList - The minuend range list.
|
|
|
|
SubtrahendList - The subtrahend range list.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to the destination (difference) PM_RANGE_LIST is
|
|
returned, otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST DstRangeList;
|
|
PLIST_ENTRY DstListEntry;
|
|
PPM_RANGE_LIST_ENTRY DstRangeListEntry;
|
|
PLIST_ENTRY SrcListEntry;
|
|
PPM_RANGE_LIST_ENTRY SrcRangeListEntry;
|
|
ULONGLONG Start;
|
|
ULONGLONG End;
|
|
PLIST_ENTRY ListEntry;
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(MinuendList != NULL);
|
|
ASSERT(SubtrahendList != NULL);
|
|
|
|
//
|
|
// Make a copy of the minuend.
|
|
//
|
|
|
|
DstRangeList = PmCopyRangeList(MinuendList);
|
|
|
|
if (DstRangeList != NULL) {
|
|
|
|
//
|
|
// Loop through each range list entry in the minuend.
|
|
//
|
|
|
|
for (DstListEntry = DstRangeList->List.Flink;
|
|
DstListEntry != &DstRangeList->List;
|
|
DstListEntry = DstListEntry->Flink) {
|
|
|
|
DstRangeListEntry = CONTAINING_RECORD(
|
|
DstListEntry,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Loop through each range list entry in the subtrahend.
|
|
//
|
|
|
|
for (SrcListEntry = SubtrahendList->List.Flink;
|
|
SrcListEntry != &SubtrahendList->List;
|
|
SrcListEntry = SrcListEntry->Flink) {
|
|
|
|
SrcRangeListEntry = CONTAINING_RECORD(
|
|
SrcListEntry,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Compute the intersection of the minuend and subtrahend
|
|
// range list entries.
|
|
//
|
|
|
|
Start = DstRangeListEntry->Start;
|
|
|
|
if (Start < SrcRangeListEntry->Start) {
|
|
Start = SrcRangeListEntry->Start;
|
|
};
|
|
|
|
End = DstRangeListEntry->End;
|
|
|
|
if (End > SrcRangeListEntry->End) {
|
|
End = SrcRangeListEntry->End;
|
|
};
|
|
|
|
if (Start > End) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// There are 4 cases:
|
|
//
|
|
// 1. The intersection overlaps the minuend range completely.
|
|
// 2. The intersection overlaps the start of the minuend
|
|
// range.
|
|
// 3. The intersection overlaps the end of the minuend range.
|
|
// 4. The intersection overlaps the middle of the minuend
|
|
// range.
|
|
//
|
|
|
|
if (DstRangeListEntry->Start == Start) {
|
|
|
|
if (DstRangeListEntry->End == End) {
|
|
|
|
//
|
|
// Case 1: Remove the minuend range list entry.
|
|
//
|
|
|
|
ListEntry = DstListEntry;
|
|
DstListEntry = DstListEntry->Blink;
|
|
RemoveEntryList(ListEntry);
|
|
PmFreeRangeListEntry(DstRangeListEntry);
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 2: Increase the minuend's start.
|
|
//
|
|
|
|
DstRangeListEntry->Start = End + 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (DstRangeListEntry->End == End) {
|
|
|
|
//
|
|
// Case 3: Decrease the minend's end.
|
|
//
|
|
|
|
DstRangeListEntry->End = Start - 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Case 4: Divide the range list entry into two
|
|
// pieces. The first range list entry's end should
|
|
// be just before the intersection start. The
|
|
// second range list entry's start should be just
|
|
// after the intersection end.
|
|
//
|
|
|
|
RangeListEntry = PmAllocateRangeListEntry();
|
|
|
|
if (RangeListEntry == NULL) {
|
|
PmFreeRangeList(DstRangeList);
|
|
return NULL;
|
|
}
|
|
|
|
RangeListEntry->Start = End + 1;
|
|
RangeListEntry->End = DstRangeListEntry->End;
|
|
|
|
//
|
|
// BUGBUG Break the list ordering but ensure that
|
|
// we'll perform the subtraction against the
|
|
// new range list entry as well.
|
|
//
|
|
|
|
InsertHeadList(
|
|
&DstRangeListEntry->ListEntry,
|
|
&RangeListEntry->ListEntry
|
|
);
|
|
|
|
DstRangeListEntry->End = Start - 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return DstRangeList;
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmIntersectRangeList(
|
|
IN PPM_RANGE_LIST SrcRangeList1,
|
|
IN PPM_RANGE_LIST SrcRangeList2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a new range list which represents the intersection
|
|
between SrcRangeList1 and SrcRangeList2.
|
|
|
|
Arguments:
|
|
|
|
SrcRangeList1, SrcRangeList - The range lists upon which to compute the
|
|
intersection.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to the destination (intersection) PM_RANGE_LIST is
|
|
returned, otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST DstRangeList;
|
|
PLIST_ENTRY SrcListEntry1;
|
|
PPM_RANGE_LIST_ENTRY SrcRangeListEntry1;
|
|
PLIST_ENTRY SrcListEntry2;
|
|
PPM_RANGE_LIST_ENTRY SrcRangeListEntry2;
|
|
ULONGLONG Start;
|
|
ULONGLONG End;
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(SrcRangeList1 != NULL);
|
|
ASSERT(SrcRangeList2 != NULL);
|
|
|
|
DstRangeList = PmAllocateRangeList();
|
|
|
|
if (DstRangeList != NULL) {
|
|
|
|
for (SrcListEntry1 = SrcRangeList1->List.Flink;
|
|
SrcListEntry1 != &SrcRangeList1->List;
|
|
SrcListEntry1 = SrcListEntry1->Flink) {
|
|
|
|
SrcRangeListEntry1 = CONTAINING_RECORD(
|
|
SrcListEntry1,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
for (SrcListEntry2 = SrcRangeList2->List.Flink;
|
|
SrcListEntry2 != &SrcRangeList2->List;
|
|
SrcListEntry2 = SrcListEntry2->Flink) {
|
|
|
|
SrcRangeListEntry2 = CONTAINING_RECORD(
|
|
SrcListEntry2,
|
|
PM_RANGE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
Start = SrcRangeListEntry1->Start;
|
|
|
|
if (Start < SrcRangeListEntry2->Start) {
|
|
Start = SrcRangeListEntry2->Start;
|
|
};
|
|
|
|
End = SrcRangeListEntry1->End;
|
|
|
|
if (End > SrcRangeListEntry2->End) {
|
|
End = SrcRangeListEntry2->End;
|
|
};
|
|
|
|
if (Start > End) {
|
|
continue;
|
|
}
|
|
|
|
RangeListEntry = PmAllocateRangeListEntry();
|
|
|
|
if (RangeListEntry == NULL) {
|
|
PmFreeRangeList(DstRangeList);
|
|
return NULL;
|
|
}
|
|
|
|
RangeListEntry->Start = Start;
|
|
RangeListEntry->End = End;
|
|
|
|
InsertTailList(
|
|
&DstRangeList->List,
|
|
&RangeListEntry->ListEntry
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return DstRangeList;
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmCreateRangeListFromCmResourceList(
|
|
IN PCM_RESOURCE_LIST CmResourceList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function converts a CM_RESOURCE_LIST to a PM_RANGE_LIST. Only
|
|
CmResourceTypeMemory descriptors are added to the PM_RANGE_LIST.
|
|
|
|
Arguments:
|
|
|
|
CmResourceList - The CM_RESOURCE_LIST to convert.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to the converted PM_RANGE_LIST is returned,
|
|
otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST RangeList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR FDesc;
|
|
ULONG FIndex;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
|
|
ULONG PIndex;
|
|
ULONGLONG Start;
|
|
ULONGLONG End;
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
RangeList = PmAllocateRangeList();
|
|
|
|
if (RangeList == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
FDesc = CmResourceList->List;
|
|
|
|
//
|
|
// Note: Any Device-Specific partial descriptor (which could be
|
|
// variably sized) is defined to be at the end and thus this code
|
|
// is safe.
|
|
//
|
|
|
|
for (FIndex = 0;
|
|
FIndex < CmResourceList->Count;
|
|
FIndex++) {
|
|
|
|
PDesc = FDesc->PartialResourceList.PartialDescriptors;
|
|
|
|
for (PIndex = 0;
|
|
PIndex < FDesc->PartialResourceList.Count;
|
|
PIndex++, PDesc++) {
|
|
|
|
//
|
|
// ISSUE Fix for ia64 (andy's change), IA32 large memory region
|
|
//
|
|
|
|
if (PDesc->Type == CmResourceTypeMemory) {
|
|
|
|
Start = PDesc->u.Memory.Start.QuadPart;
|
|
End = Start + PDesc->u.Memory.Length - 1;
|
|
|
|
RangeListEntry = PmAllocateRangeListEntry();
|
|
|
|
if (RangeListEntry == NULL) {
|
|
PmFreeRangeList(RangeList);
|
|
return NULL;
|
|
}
|
|
|
|
RangeListEntry->Start = Start;
|
|
RangeListEntry->End = End;
|
|
|
|
InsertTailList(
|
|
&RangeList->List,
|
|
&RangeListEntry->ListEntry
|
|
);
|
|
}
|
|
}
|
|
|
|
FDesc = (PCM_FULL_RESOURCE_DESCRIPTOR)PDesc;
|
|
}
|
|
|
|
return RangeList;
|
|
}
|
|
|
|
PPM_RANGE_LIST
|
|
PmCreateRangeListFromPhysicalMemoryRanges(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls MmGetPhysicalRanges and converts the returned
|
|
PHYSICAL_MEMORY_RANGE list to a PM_RANGE_LIST.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Upon success a pointer to the converted PM_RANGE_LIST is returned,
|
|
otherwise NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPM_RANGE_LIST RangeList;
|
|
PPHYSICAL_MEMORY_RANGE MemoryRange;
|
|
ULONG Index;
|
|
ULONGLONG Start;
|
|
ULONGLONG End;
|
|
PPM_RANGE_LIST_ENTRY RangeListEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
RangeList = PmAllocateRangeList();
|
|
|
|
if (RangeList != NULL) {
|
|
|
|
MemoryRange = MmGetPhysicalMemoryRanges();
|
|
|
|
if (MemoryRange == NULL) {
|
|
|
|
PmFreeRangeList(RangeList);
|
|
RangeList = NULL;
|
|
|
|
} else {
|
|
|
|
for (Index = 0;
|
|
MemoryRange[Index].NumberOfBytes.QuadPart != 0;
|
|
Index++) {
|
|
|
|
Start = MemoryRange[Index].BaseAddress.QuadPart;
|
|
End = Start + (MemoryRange[Index].NumberOfBytes.QuadPart - 1);
|
|
|
|
RangeListEntry = PmAllocateRangeListEntry();
|
|
|
|
if (RangeListEntry == NULL) {
|
|
PmFreeRangeList(RangeList);
|
|
ExFreePool(MemoryRange);
|
|
return NULL;
|
|
}
|
|
|
|
RangeListEntry->Start = Start;
|
|
RangeListEntry->End = End;
|
|
|
|
InsertTailList(
|
|
&RangeList->List,
|
|
&RangeListEntry->ListEntry
|
|
);
|
|
|
|
}
|
|
|
|
ExFreePool(MemoryRange);
|
|
|
|
}
|
|
}
|
|
|
|
return RangeList;
|
|
}
|