/*++ Copyright (c) 2024, Quinn Stephens. Provided under the BSD 3-Clause license. Module Name: mmmd.c Abstract: Provides memory descriptor routines. --*/ #include #include "bootlib.h" #include "mm.h" #define MAX_STATIC_DESCRIPTOR_COUNT 1024 MEMORY_DESCRIPTOR MmStaticMemoryDescriptors[MAX_STATIC_DESCRIPTOR_COUNT]; PMEMORY_DESCRIPTOR MmGlobalMemoryDescriptors; ULONG MmGlobalMemoryDescriptorCount, MmGlobalMemoryDescriptorsUsed; PMEMORY_DESCRIPTOR MmDynamicMemoryDescriptors; ULONG MmDynamicMemoryDescriptorCount, MmDynamicMemoryDescriptorsUsed; #define MAX_PRECEDENCE_INDEX sizeof(MmPlatformMemoryTypePrecedence) / sizeof(MmPlatformMemoryTypePrecedence[0]) MEMORY_TYPE MmPlatformMemoryTypePrecedence[] = { MEMORY_TYPE_RESERVED, MEMORY_TYPE_UNUSABLE, MEMORY_TYPE_MMIO, MEMORY_TYPE_MMIO_PORT_SPACE, MEMORY_TYPE_PAL_CODE, MEMORY_TYPE_RUNTIME_SERVICES_CODE, MEMORY_TYPE_RUNTIME_SERVICES_DATA, MEMORY_TYPE_ACPI_NVS, MEMORY_TYPE_ACPI_RECLAIM, MEMORY_TYPE_PERSISTENT, MEMORY_TYPE_BOOT_APPLICATION_2, MEMORY_TYPE_BOOT_SERVICES, MEMORY_TYPE_FREE, MEMORY_TYPE_FREE_ZEROED }; ULONG GetPrecedenceIndex ( IN MEMORY_TYPE Type ) /*++ Routine Description: Finds the index into MmPlatformMemoryTypePrecedence for Type. Arguments: Type - The memory type. Return Value: The precedence index if found. MAX_PRECEDENCE_INDEX if not found. --*/ { for (ULONG Index = 0; Index < MAX_PRECEDENCE_INDEX; Index++) { if (MmPlatformMemoryTypePrecedence[Index] == Type) { return Index; } } return MAX_PRECEDENCE_INDEX; } BOOLEAN MmMdpHasPrecedence ( IN MEMORY_TYPE TypeA, IN MEMORY_TYPE TypeB ) /*++ Routine Description: Compares two memory types to determine which has precedence. Arguments: TypeA - Memory type A. TypeB - Memory type B. Return Value: TRUE if TypeA has precedence over TypeB, or if neither has precedence. FALSE if TypeB has precedence over TypeA. --*/ { ULONG ClassA, ClassB; ULONG PrecedenceIndexA, PrecedenceIndexB; if (TypeB == MEMORY_TYPE_FREE_ZEROED) { return TRUE; } else if (TypeA == MEMORY_TYPE_FREE_ZEROED) { return FALSE; } else if (TypeB == MEMORY_TYPE_FREE) { return TRUE; } else if (TypeA == MEMORY_TYPE_FREE) { return FALSE; } ClassA = TypeA >> 28; ClassB = TypeB >> 28; if (ClassA != MEMORY_CLASS_APPLICATION && ClassA != MEMORY_CLASS_LIBRARY && ClassA != MEMORY_CLASS_SYSTEM) { return TRUE; } else if (ClassB != MEMORY_CLASS_APPLICATION && ClassB != MEMORY_CLASS_LIBRARY && ClassB != MEMORY_CLASS_SYSTEM) { return FALSE; } if (ClassA != MEMORY_CLASS_SYSTEM) { return (ClassB != MEMORY_CLASS_SYSTEM && (ClassA == MEMORY_CLASS_APPLICATION || ClassB != MEMORY_CLASS_APPLICATION)) ? TRUE:FALSE; } else if (ClassB != MEMORY_CLASS_SYSTEM) { return TRUE; } PrecedenceIndexA = GetPrecedenceIndex(TypeA); if (PrecedenceIndexA == MAX_PRECEDENCE_INDEX) { return TRUE; } PrecedenceIndexB = GetPrecedenceIndex(TypeB); if (PrecedenceIndexB == MAX_PRECEDENCE_INDEX) { return FALSE; } return PrecedenceIndexA <= PrecedenceIndexB ? TRUE:FALSE; } BOOLEAN MmMdpTruncateDescriptor ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN PMEMORY_DESCRIPTOR Descriptor, IN ULONG Flags ) /*++ Routine Description: Trunactes a memory descriptor that overlaps with adjacent descriptors. Arguments: Mdl - The MDL containing Descriptor. Descriptor - The descriptor to truncate. Return Value: TRUE if Descriptor was deleted from Mdl. --*/ { NTSTATUS Status; PMEMORY_DESCRIPTOR PrevDescriptor, NextDescriptor, NewDescriptor; ULONGLONG DescriptorEnd, PrevDescriptorEnd, NextDescriptorEnd; ULONGLONG MappedFirstPage; ULONGLONG Change; PrevDescriptor = (PMEMORY_DESCRIPTOR)Descriptor->ListEntry.Blink; NextDescriptor = (PMEMORY_DESCRIPTOR)Descriptor->ListEntry.Flink; DescriptorEnd = Descriptor->FirstPage + Descriptor->PageCount; PrevDescriptorEnd = PrevDescriptor->FirstPage + PrevDescriptor->PageCount; NextDescriptorEnd = NextDescriptor->FirstPage + NextDescriptor->PageCount; // // Check if overlapping with previous descriptor. // if (Descriptor->ListEntry.Blink != Mdl->Head && Descriptor->FirstPage < PrevDescriptorEnd) { if (MmMdpHasPrecedence(Descriptor->Type, PrevDescriptor->Type)) { PrevDescriptor->PageCount = Descriptor->FirstPage - PrevDescriptor->FirstPage; if (DescriptorEnd < PrevDescriptorEnd) { if (PrevDescriptor->MappedFirstPage) { MappedFirstPage = PrevDescriptor->MappedFirstPage + DescriptorEnd - PrevDescriptor->FirstPage; } else { MappedFirstPage = 0; } NewDescriptor = MmMdInitDescriptor( DescriptorEnd, MappedFirstPage, PrevDescriptorEnd - DescriptorEnd, PrevDescriptor->Attributes, PrevDescriptor->Type ); if (NewDescriptor != NULL) { Status = MmMdAddDescriptorToList(Mdl, NewDescriptor, Flags); if (!NT_SUCCESS(Status)) { MmMdFreeDescriptor(NewDescriptor); } } } // // Delete and free the previous // descriptor if it is now empty. // if (PrevDescriptor->PageCount == 0) { MmMdRemoveDescriptorFromList(Mdl, PrevDescriptor); MmMdFreeDescriptor(PrevDescriptor); } } else { // // Remove if completely overlapping. // if (DescriptorEnd <= PrevDescriptorEnd) { MmMdRemoveDescriptorFromList(Mdl, Descriptor); MmMdFreeDescriptor(Descriptor); return TRUE; } // // Otherwise, move the descriptor. // Change = PrevDescriptorEnd - Descriptor->FirstPage; Descriptor->FirstPage += Change; Descriptor->PageCount -= Change; if (Descriptor->MappedFirstPage) { Descriptor->MappedFirstPage += Change; } } } // // Check if overlapping with next descriptor. // if (Descriptor->ListEntry.Flink != Mdl->Head && NextDescriptor->FirstPage < DescriptorEnd) { if (MmMdpHasPrecedence(NextDescriptor->Type, Descriptor->Type)) { Descriptor->PageCount = NextDescriptor->FirstPage - Descriptor->FirstPage; if (NextDescriptorEnd < DescriptorEnd) { if (Descriptor->MappedFirstPage) { MappedFirstPage = Descriptor->MappedFirstPage + NextDescriptorEnd - Descriptor->FirstPage; } else { MappedFirstPage = 0; } NewDescriptor = MmMdInitDescriptor( NextDescriptorEnd, MappedFirstPage, DescriptorEnd - NextDescriptorEnd, Descriptor->Attributes, Descriptor->Type ); if (NewDescriptor != NULL) { Status = MmMdAddDescriptorToList(Mdl, NewDescriptor, Flags); if (!NT_SUCCESS(Status)) { MmMdFreeDescriptor(NewDescriptor); } } } // // Delete and free the previous // descriptor if it is now empty. // if (Descriptor->PageCount == 0) { MmMdRemoveDescriptorFromList(Mdl, Descriptor); MmMdFreeDescriptor(Descriptor); } } else { // // Remove if completely overlapping. // if (NextDescriptorEnd <= DescriptorEnd) { MmMdRemoveDescriptorFromList(Mdl, NextDescriptor); MmMdFreeDescriptor(NextDescriptor); return TRUE; } // // Otherwise, move the next descriptor. // Change = DescriptorEnd - NextDescriptor->FirstPage; NextDescriptor->FirstPage += Change; NextDescriptor->PageCount -= Change; if (NextDescriptor->MappedFirstPage) { NextDescriptor->MappedFirstPage += Change; } } } return FALSE; } NTSTATUS MmMdAddDescriptorToList ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN PMEMORY_DESCRIPTOR Descriptor, IN ULONG Flags ) /*++ Routine Description: Adds a descriptor to a MDL. Arguments: Mdl - the MDL to add Descriptor to. Descriptor - the descriptor to add to Mdl. Return Value: STATUS_SUCCESS if successful, STATUS_INVALID_PARAMETER if Mdl or Descriptor are invalid. --*/ { PLIST_ENTRY Entry; PMEMORY_DESCRIPTOR CurrentDescriptor; if (Mdl == NULL || Descriptor == NULL) { DebugPrint(L"MmMdAddDescriptorToList(): Mdl and/or Descriptor are NULL\r\n"); return STATUS_INVALID_PARAMETER; } if (Mdl->Current) { if (Descriptor->FirstPage < ((PMEMORY_DESCRIPTOR)Mdl->Current)->FirstPage) { Entry = Mdl->Head->Flink; } else { Entry = Mdl->Current; } } else { Entry = Mdl->Head->Flink; } // // Search for an existing descriptor for this region. // while (Entry != Mdl->Head) { CurrentDescriptor = (PMEMORY_DESCRIPTOR)Entry; if ( Descriptor->FirstPage >= CurrentDescriptor->FirstPage && (Descriptor->FirstPage != CurrentDescriptor->FirstPage || !MmMdpHasPrecedence(Descriptor->Type, CurrentDescriptor->Type)) ) { Entry = Entry->Flink; continue; } // // Insert descriptor into the list // right before the current entry. // Descriptor->ListEntry.Blink = Entry->Blink; Descriptor->ListEntry.Flink = Entry; Entry->Blink->Flink = &Descriptor->ListEntry; Entry->Blink = &Descriptor->ListEntry; // // Truncate overlapping descriptors // into one larger descriptor. // if (Flags & MDL_OPERATION_FLAGS_TRUNCATE) { MmMdpTruncateDescriptor(Mdl, Descriptor, Flags); } return STATUS_SUCCESS; } // // If there are no existing descriptors for this region // (or the list is empty), insert and truncate the descriptor. // InsertTailList(Mdl->Head, &Descriptor->ListEntry); if (Flags & MDL_OPERATION_FLAGS_TRUNCATE) { MmMdpTruncateDescriptor(Mdl, Descriptor, Flags); } return STATUS_SUCCESS; } VOID MmMdRemoveDescriptorFromList ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN PMEMORY_DESCRIPTOR Descriptor ) /*++ Routine Description: Removes a descriptor from a MDL. Arguments: Mdl - MDL to remove Descriptor from. Descriptor - Descriptor to remove from Mdl. Return Value: None. --*/ { PLIST_ENTRY Blink; Blink = Descriptor->ListEntry.Blink; RemoveEntryList(&Descriptor->ListEntry); // // Check if the removed descriptor was cached. // if (Mdl->Current != &Descriptor->ListEntry) { return; } // // Cache the previous descriptor if possible. // if ( ( (ULONG_PTR)Blink < (ULONG_PTR)MmGlobalMemoryDescriptors || (ULONG_PTR)Blink >= (ULONG_PTR)&MmGlobalMemoryDescriptors[MmGlobalMemoryDescriptorCount] ) && Blink != Mdl->Head ) { Mdl->Current = Blink; } else { Mdl->Current = NULL; } } PMEMORY_DESCRIPTOR MmMdFindDescriptorFromMdl ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN ULONGLONG Page, IN ULONG Flags ) /*++ Routine Description: Searches an MDL for the descriptor containing a page. Arguments: Mdl - The MDL to search. Page - The page to search for. Flags - MDL_OPERATION_FLAGS_* MDL_OPERATION_FLAGS_PHYSICAL if Page is physical. MDL_OPERATION_FLAGS_VIRTUAL if Page is virtual. Return Value: Pointer to the descriptor if successful. NULL if an error occurs. --*/ { BOOLEAN Mapped; PMEMORY_DESCRIPTOR Descriptor; PLIST_ENTRY ListEntry; ULONGLONG FirstPage; Mapped = FALSE; if (Flags & MDL_OPERATION_FLAGS_VIRTUAL) { if (Mdl->Type == MDL_TYPE_PHYSICAL) { Mapped = TRUE; } } else { // // If the MDL is virtual, the // virtual flag must be set. // if (Mdl->Type == MDL_TYPE_VIRTUAL) { DebugPrint(L"MmMdFindDescriptorFromMdl(): Flags is invalid\r\n"); return NULL; } } // // Check if the cached descriptor is in range. // if (!Mapped && Mdl->Current != NULL) { Descriptor = (PMEMORY_DESCRIPTOR)Mdl->Current; if (Page < Descriptor->FirstPage) { ListEntry = Mdl->Head->Flink; } else { ListEntry = Mdl->Current; } } else { ListEntry = Mdl->Head->Flink; } while (ListEntry != Mdl->Head) { Descriptor = (PMEMORY_DESCRIPTOR)ListEntry; if (Mapped) { FirstPage = Descriptor->MappedFirstPage; } else { FirstPage = Descriptor->FirstPage; } // // Check if this descriptor contains Page. // if ((!Mapped || FirstPage) && Page >= FirstPage && Page < FirstPage + Descriptor->PageCount) { return Descriptor; } ListEntry = ListEntry->Flink; } return NULL; } NTSTATUS MmMdRemoveRegionFromMdlEx ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN ULONGLONG RemoveStart, IN ULONGLONG PageCount, IN ULONG Flags, OUT PMEMORY_DESCRIPTOR_LIST Unused ) /*++ Routine Description: Removes a region from a MDL. Arguments: Mdl - MDL to remove the region from. RemoveStart - The first page in the region. PageCount - The number of pages in the region. Flags - MDL_OPERATION_FLAGS_*. MDL_OPERATION_FLAGS_PHYSICAL if the region is physical. MDL_OPERATION_FLAGS_VIRTUAL if the region is virtual. Unused - Unused. Return Value: STATUS_SUCCESS if successful, STATUS_INVALID_PARAMETER if Flags value is invalid. --*/ { NTSTATUS Status; ULONG Offset; BOOLEAN Mapped; ULONGLONG RemoveEnd; PLIST_ENTRY ListEntry; ULONGLONG DescriptorStart, DescriptorEnd; PMEMORY_DESCRIPTOR Descriptor, NewDescriptor; (VOID)Unused; Mapped = FALSE; if (Flags & MDL_OPERATION_FLAGS_VIRTUAL) { if (Mdl->Type == MDL_TYPE_PHYSICAL) { Mapped = TRUE; } } else { // // If the MDL is virtual, the // virtual flag must be set. // if (Mdl->Type == MDL_TYPE_VIRTUAL) { DebugPrint(L"MmMdRemoveRegionFromMdlEx(): Flags is invalid\r\n"); return STATUS_INVALID_PARAMETER; } } RemoveEnd = RemoveStart + PageCount; ListEntry = Mdl->Head->Flink; while (ListEntry != Mdl->Head) { Descriptor = (PMEMORY_DESCRIPTOR)ListEntry; if (Mapped) { DescriptorStart = Descriptor->MappedFirstPage; } else { DescriptorStart = Descriptor->FirstPage; } DescriptorEnd = DescriptorStart + Descriptor->PageCount; // // Check if the region to be removed // is inside the current descriptor. // if (RemoveStart <= DescriptorStart && RemoveEnd > DescriptorStart) { // // The region is around the start of the descriptor, or // they have identical locations and sizes. // | RemoveStart | DescriptorStart | RemoveEnd | DescriptorEnd | // | Lower address ............................ Higher address | // if (RemoveEnd < DescriptorEnd) { Offset = RemoveEnd - DescriptorStart; } else { Offset = DescriptorEnd - DescriptorStart; } // // Shrink the descriptor. // Descriptor->FirstPage += Offset; Descriptor->PageCount -= Offset; if (Descriptor->MappedFirstPage) { Descriptor->MappedFirstPage += Offset; } // // Remove descriptor if now empty. // if (Descriptor->PageCount == 0) { MmMdRemoveDescriptorFromList(Mdl, Descriptor); MmMdFreeDescriptor(Descriptor); } } else if (RemoveStart < DescriptorEnd && RemoveEnd >= DescriptorEnd) { // // The region is around the end of the descriptor. // | DescriptorStart | RemoveStart | DescriptorEnd | RemoveEnd | // | Lower address ............................ Higher address | // // // Simply shrink the descriptor. // Descriptor->PageCount -= DescriptorEnd - RemoveStart; } else if (RemoveStart > DescriptorStart && RemoveEnd < DescriptorEnd) { // // The region is completely inside the descriptor. // In this case, the descriptor must be split in two. // | DescriptorStart | RemoveStart | RemoveEnd | DescriptorEnd | // | Lower address ............................ Higher address | // // // Create a new descriptor before the removed region. // NewDescriptor = MmMdInitDescriptor( Descriptor->FirstPage, Descriptor->MappedFirstPage, RemoveStart - DescriptorStart, Descriptor->Attributes, Descriptor->Type ); // // Shrink and move the current descriptor. // Offset = NewDescriptor->PageCount + PageCount; Descriptor->FirstPage += Offset; Descriptor->PageCount -= Offset; if (Descriptor->MappedFirstPage) { Descriptor->MappedFirstPage += Offset; } // // Now check if MmMdInitDescriptor() actually worked. // if (NewDescriptor == NULL) { return STATUS_NO_MEMORY; } Status = MmMdAddDescriptorToList(Mdl, NewDescriptor, Flags); if (!NT_SUCCESS(Status)) { MmMdFreeDescriptor(NewDescriptor); return Status; } } ListEntry = ListEntry->Flink; } return Status; } NTSTATUS MmMdRemoveRegionFromMdl ( IN PMEMORY_DESCRIPTOR_LIST Mdl, IN ULONGLONG RemoveStart, IN ULONGLONG PageCount, IN ULONG Flags ) /*++ Routine Description: Removes a region from a MDL. Wrapper around MmMdRemoveRegionFromMdlEx(). Arguments: Same as MmMdRemoveRegionFromMdlEx(), except for Unused. Return Value: Any status code returned by MmMdRemoveRegionFromMdlEx(). --*/ { return MmMdRemoveRegionFromMdlEx( Mdl, RemoveStart, PageCount, Flags, NULL ); } NTSTATUS MmMdFreeDescriptor ( IN PMEMORY_DESCRIPTOR Descriptor ) /*++ Routine Description: Frees a memory descriptor. Arguments: Descriptor - the descriptor to free. Return Value: STATUS_SUCCESS if successful, other NTSTATUS value if an error occurs. --*/ { if ( ( MmDynamicMemoryDescriptors != NULL && (ULONG_PTR)Descriptor >= (ULONG_PTR)MmDynamicMemoryDescriptors && (ULONG_PTR)Descriptor <= (ULONG_PTR)&MmDynamicMemoryDescriptors[MmDynamicMemoryDescriptorCount] ) || ( (ULONG_PTR)Descriptor >= (ULONG_PTR)MmStaticMemoryDescriptors && (ULONG_PTR)Descriptor <= (ULONG_PTR)&MmStaticMemoryDescriptors[MAX_STATIC_DESCRIPTOR_COUNT] ) ) { // // Clear the descriptor from static/dynamic MDL. // RtlZeroMemory(Descriptor, sizeof(MEMORY_DESCRIPTOR)); return STATUS_SUCCESS; } // // TODO: Free the descriptor from the heap. // Need BlMmFreeHeap(). // DebugPrint(L"MmMdFreeDescriptor(): Heap not available\r\n"); return STATUS_NOT_IMPLEMENTED; // return BlMmFreeHeap(Descriptor); } VOID MmMdFreeList ( IN PMEMORY_DESCRIPTOR_LIST Mdl ) /*++ Routine Description: Frees a memory descriptor list (MDL). Arguments: Mdl - the MDL. Return Value: None. --*/ { PLIST_ENTRY Entry; Entry = Mdl->Head->Flink; while (Entry != Mdl->Head) { MmMdRemoveDescriptorFromList(Mdl, (PMEMORY_DESCRIPTOR)Entry); MmMdFreeDescriptor((PMEMORY_DESCRIPTOR)Entry); Entry = Entry->Flink; } } PMEMORY_DESCRIPTOR MmMdInitDescriptor ( IN ULONGLONG FirstPage, IN ULONGLONG MappedFirstPage, IN ULONGLONG PageCount, IN ULONG Attributes, IN MEMORY_TYPE Type ) /*++ Routine Description: Initializes a memory descriptor. Arguments: FirstPage - The first page in the region. MappedFirstPage - The first page in the mapping. Attributes - Memory attributes of the region. Type - Memory type of the region. Return Value: Pointer to the memory descriptor, NULL if an error occurs. --*/ { PMEMORY_DESCRIPTOR Descriptor; if (MmGlobalMemoryDescriptorsUsed >= MmGlobalMemoryDescriptorCount) { DebugPrint(L"MmMdInitDescriptor(): No free descriptors available\r\n"); return NULL; } Descriptor = &MmGlobalMemoryDescriptors[MmGlobalMemoryDescriptorsUsed++]; Descriptor->FirstPage = FirstPage; Descriptor->MappedFirstPage = MappedFirstPage; Descriptor->PageCount = PageCount; Descriptor->Attributes = Attributes; Descriptor->Type = Type; InitializeListHead(&Descriptor->ListEntry); return Descriptor; } NTSTATUS MmMdDestroy ( ) /*++ Routine Description: Cleans up after any actions performed by the memory descriptor manager. After calling this, the memory descriptor manager can no longer be used. Arguments: None. Return Value: STATUS_SUCCESS. --*/ { // // TODO: Implement this routine. // return STATUS_SUCCESS; } VOID MmMdInitialize ( IN ULONG Stage, IN PBOOT_LIBRARY_PARAMETERS LibraryParameters ) /*++ Routine Description: Initializes the memory descriptor manager. Arguments: Stage - Which stage of initialization to perform. Stage 0: Initializes the static memory descriptor pool. LibraryParameters - Pointer to the library parameters structure. Return Value: None. --*/ { (VOID)LibraryParameters; if (Stage == 0) { // // Initialize static memory descriptor pool. // MmGlobalMemoryDescriptors = &MmStaticMemoryDescriptors[0]; MmGlobalMemoryDescriptorCount = MAX_STATIC_DESCRIPTOR_COUNT; MmGlobalMemoryDescriptorsUsed = 0; RtlZeroMemory(MmGlobalMemoryDescriptors, MAX_STATIC_DESCRIPTOR_COUNT * sizeof(MEMORY_DESCRIPTOR)); } // // TODO: Implement stage 1 initialization. // }