/*++ 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; }