mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
1702 lines
55 KiB
C
1702 lines
55 KiB
C
/*
|
|
* COPYRIGHT: See COPYING.ARM in the top level directory
|
|
* PROJECT: ReactOS UEFI Boot Library
|
|
* FILE: boot/environ/lib/mm/pagealloc.c
|
|
* PURPOSE: Boot Library Memory Manager Page Allocator
|
|
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "bl.h"
|
|
#include "bcd.h"
|
|
|
|
typedef struct _BL_PA_REQUEST
|
|
{
|
|
BL_ADDRESS_RANGE BaseRange;
|
|
BL_ADDRESS_RANGE VirtualRange;
|
|
ULONG Type;
|
|
ULONGLONG Pages;
|
|
ULONG MemoryType;
|
|
ULONG Alignment;
|
|
ULONG Flags;
|
|
} BL_PA_REQUEST, *PBL_PA_REQUEST;
|
|
|
|
/* DATA VARIABLES ************************************************************/
|
|
|
|
extern ULONG MmArchLargePageSize;
|
|
|
|
ULONGLONG PapMaximumPhysicalPage, PapMinimumPhysicalPage;
|
|
|
|
ULONG PapMinimumAllocationCount;
|
|
|
|
BOOLEAN PapInitializationStatus;
|
|
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlMappedAllocated;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlMappedUnallocated;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlFwAllocationTracker;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlUnmappedAllocated;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlUnmappedUnallocated;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlReservedAllocated;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlBadMemory;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlTruncatedMemory;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlPersistentMemory;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlCompleteBadMemory;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlFreeVirtual;
|
|
BL_MEMORY_DESCRIPTOR_LIST MmMdlMappingTrackers;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
NTSTATUS
|
|
MmPaTruncateMemory (
|
|
_In_ ULONGLONG BasePage
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Increase nesting depth */
|
|
++MmDescriptorCallTreeCount;
|
|
|
|
/* Set the maximum page to the truncated request */
|
|
if (BasePage < PapMaximumPhysicalPage)
|
|
{
|
|
PapMaximumPhysicalPage = BasePage;
|
|
}
|
|
|
|
/* Truncate mapped and allocated memory */
|
|
Status = MmMdTruncateDescriptors(&MmMdlMappedAllocated,
|
|
&MmMdlTruncatedMemory,
|
|
BasePage);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Truncate unmapped and allocated memory */
|
|
Status = MmMdTruncateDescriptors(&MmMdlUnmappedAllocated,
|
|
&MmMdlTruncatedMemory,
|
|
BasePage);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Truncate mapped and unallocated memory */
|
|
Status = MmMdTruncateDescriptors(&MmMdlMappedUnallocated,
|
|
&MmMdlTruncatedMemory,
|
|
BasePage);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Truncate unmapped and unallocated memory */
|
|
Status = MmMdTruncateDescriptors(&MmMdlUnmappedUnallocated,
|
|
&MmMdlTruncatedMemory,
|
|
BasePage);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Truncate reserved memory */
|
|
Status = MmMdTruncateDescriptors(&MmMdlReservedAllocated,
|
|
&MmMdlTruncatedMemory,
|
|
BasePage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Restore the nesting depth */
|
|
MmMdFreeGlobalDescriptors();
|
|
--MmDescriptorCallTreeCount;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BlpMmInitializeConstraints (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status, ReturnStatus;
|
|
ULONGLONG LowestAddressValid, HighestAddressValid;
|
|
ULONGLONG LowestPage, HighestPage;
|
|
|
|
/* Assume success */
|
|
ReturnStatus = STATUS_SUCCESS;
|
|
|
|
/* Check for LOWMEM */
|
|
Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
|
|
BcdLibraryInteger_AvoidLowPhysicalMemory,
|
|
&LowestAddressValid);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Align the address */
|
|
LowestAddressValid = (ULONG_PTR)PAGE_ALIGN(LowestAddressValid);
|
|
LowestPage = LowestAddressValid >> PAGE_SHIFT;
|
|
|
|
/* Make sure it's below 4GB */
|
|
if (LowestPage <= 0x100000)
|
|
{
|
|
PapMinimumPhysicalPage = LowestPage;
|
|
}
|
|
}
|
|
|
|
/* Check for MAXMEM */
|
|
Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
|
|
BcdLibraryInteger_TruncatePhysicalMemory,
|
|
&HighestAddressValid);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Get the page */
|
|
HighestPage = HighestAddressValid >> PAGE_SHIFT;
|
|
|
|
/* Truncate memory above this page */
|
|
ReturnStatus = MmPaTruncateMemory(HighestPage);
|
|
}
|
|
|
|
/* Return back to the caller */
|
|
return ReturnStatus;
|
|
}
|
|
|
|
PWCHAR
|
|
MmMdListPointerToName (_In_ PBL_MEMORY_DESCRIPTOR_LIST MdList)
|
|
{
|
|
if (MdList == &MmMdlUnmappedAllocated)
|
|
{
|
|
return L"UnmapAlloc";
|
|
}
|
|
else if (MdList == &MmMdlUnmappedUnallocated)
|
|
{
|
|
return L"UnmapUnalloc";
|
|
}
|
|
else if (MdList == &MmMdlMappedAllocated)
|
|
{
|
|
return L"MapAlloc";
|
|
}
|
|
else if (MdList == &MmMdlMappedUnallocated)
|
|
{
|
|
return L"MapUnalloc";
|
|
}
|
|
else
|
|
{
|
|
return L"Other";
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapAllocateRegionFromMdl (
|
|
_In_ PBL_MEMORY_DESCRIPTOR_LIST NewList,
|
|
_Out_opt_ PBL_MEMORY_DESCRIPTOR Descriptor,
|
|
_In_ PBL_MEMORY_DESCRIPTOR_LIST CurrentList,
|
|
_In_ PBL_PA_REQUEST Request,
|
|
_In_ BL_MEMORY_TYPE Type
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BL_MEMORY_DESCRIPTOR LocalDescriptor = {{0}};
|
|
PBL_MEMORY_DESCRIPTOR FoundDescriptor, TempDescriptor;
|
|
PLIST_ENTRY ListHead, NextEntry;
|
|
BOOLEAN TopDown, GotFwPages;
|
|
EFI_PHYSICAL_ADDRESS EfiAddress;
|
|
ULONGLONG LocalEndPage, FoundEndPage, LocalVirtualEndPage;
|
|
|
|
/* Check if any parameters were not passed in correctly */
|
|
if (!(CurrentList) || !(Request) || (!(NewList) && !(Descriptor)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Set failure by default */
|
|
Status = STATUS_NO_MEMORY;
|
|
|
|
/* Take the head and next entry in the list, as appropriate */
|
|
ListHead = CurrentList->First;
|
|
if (Request->Type & BL_MM_REQUEST_TOP_DOWN_TYPE)
|
|
{
|
|
NextEntry = ListHead->Blink;
|
|
TopDown = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NextEntry = ListHead->Flink;
|
|
TopDown = FALSE;
|
|
}
|
|
|
|
/* Loop through the list */
|
|
GotFwPages = FALSE;
|
|
while (NextEntry != ListHead)
|
|
{
|
|
/* Grab a descriptor */
|
|
FoundDescriptor = CONTAINING_RECORD(NextEntry,
|
|
BL_MEMORY_DESCRIPTOR,
|
|
ListEntry);
|
|
|
|
/* See if it matches the request */
|
|
if (MmMdFindSatisfyingRegion(FoundDescriptor,
|
|
&LocalDescriptor,
|
|
Request->Pages,
|
|
&Request->BaseRange,
|
|
&Request->VirtualRange,
|
|
TopDown,
|
|
Request->MemoryType,
|
|
Request->Flags,
|
|
Request->Alignment))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* It doesn't, move to the next appropriate entry */
|
|
if (TopDown)
|
|
{
|
|
NextEntry = NextEntry->Blink;
|
|
}
|
|
else
|
|
{
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
}
|
|
|
|
/* Check if we exhausted the list */
|
|
if (NextEntry == ListHead)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Copy all the flags that are not request flag */
|
|
LocalDescriptor.Flags = (Request->Flags & 0xFFFF0000) |
|
|
(LocalDescriptor.Flags & 0x0000FFFF);
|
|
|
|
/* Are we using the physical memory list, and are we OK with using firmware? */
|
|
if ((CurrentList == &MmMdlUnmappedUnallocated) &&
|
|
!((Request->Flags & BlMemoryNonFirmware) ||
|
|
(LocalDescriptor.Flags & BlMemoryNonFirmware)))
|
|
{
|
|
/* Allocate the requested address from EFI */
|
|
EfiAddress = LocalDescriptor.BasePage << PAGE_SHIFT;
|
|
Status = EfiAllocatePages(AllocateAddress,
|
|
(ULONG)LocalDescriptor.PageCount,
|
|
&EfiAddress);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EfiPrintf(L"EFI memory allocation failure\r\n");
|
|
EfiStall(10000000);
|
|
return Status;
|
|
}
|
|
|
|
/* Remember we got memory from EFI */
|
|
GotFwPages = TRUE;
|
|
}
|
|
|
|
/* Remove the descriptor from the original list it was on */
|
|
MmMdRemoveDescriptorFromList(CurrentList, FoundDescriptor);
|
|
|
|
/* Get the end pages */
|
|
LocalEndPage = LocalDescriptor.PageCount + LocalDescriptor.BasePage;
|
|
FoundEndPage = FoundDescriptor->PageCount + FoundDescriptor->BasePage;
|
|
|
|
/* Are we allocating from the virtual memory list? */
|
|
if (CurrentList == &MmMdlMappedUnallocated)
|
|
{
|
|
/* Check if the region matches perfectly */
|
|
if ((LocalDescriptor.BasePage == FoundDescriptor->BasePage) &&
|
|
(LocalEndPage == FoundEndPage))
|
|
{
|
|
/* Check if the original descriptor had the flag set */
|
|
if ((FoundDescriptor->Flags & 0x40000000) && (Descriptor))
|
|
{
|
|
/* Make our local one have it too, even if not needed */
|
|
LocalDescriptor.Flags |= 0x40000000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Write the 'incomplete mapping' flag */
|
|
FoundDescriptor->Flags |= 0x40000000;
|
|
if (Descriptor)
|
|
{
|
|
/* Including on the local one if there's one passed in */
|
|
LocalDescriptor.Flags |= 0x40000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Does the memory we received not exactly fall onto the beginning of its descriptor? */
|
|
if (LocalDescriptor.BasePage != FoundDescriptor->BasePage)
|
|
{
|
|
TempDescriptor = MmMdInitByteGranularDescriptor(FoundDescriptor->Flags,
|
|
FoundDescriptor->Type,
|
|
FoundDescriptor->BasePage,
|
|
FoundDescriptor->VirtualPage,
|
|
LocalDescriptor.BasePage -
|
|
FoundDescriptor->BasePage);
|
|
Status = MmMdAddDescriptorToList(CurrentList, TempDescriptor, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Does the memory we received not exactly fall onto the end of its descriptor? */
|
|
LocalVirtualEndPage = LocalDescriptor.VirtualPage ?
|
|
LocalDescriptor.VirtualPage + LocalDescriptor.PageCount : 0;
|
|
if (LocalEndPage != FoundEndPage)
|
|
{
|
|
TempDescriptor = MmMdInitByteGranularDescriptor(FoundDescriptor->Flags,
|
|
FoundDescriptor->Type,
|
|
LocalEndPage,
|
|
LocalVirtualEndPage,
|
|
FoundEndPage - LocalEndPage);
|
|
Status = MmMdAddDescriptorToList(CurrentList, TempDescriptor, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* We got the memory we needed */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Are we supposed to insert it into a new list? */
|
|
if (NewList)
|
|
{
|
|
/* Copy the allocated region descriptor into the one we found */
|
|
FoundDescriptor->BaseAddress = LocalDescriptor.BaseAddress;
|
|
FoundDescriptor->VirtualPage = LocalDescriptor.VirtualPage;
|
|
FoundDescriptor->PageCount = LocalDescriptor.PageCount;
|
|
FoundDescriptor->Type = Type;
|
|
FoundDescriptor->Flags = LocalDescriptor.Flags;
|
|
|
|
/* Remember if it came from EFI */
|
|
if (GotFwPages)
|
|
{
|
|
FoundDescriptor->Flags |= BlMemoryFirmware;
|
|
}
|
|
|
|
/* Add the descriptor to the requested list */
|
|
Status = MmMdAddDescriptorToList(NewList, FoundDescriptor, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Free the descriptor, nobody wants to know about it anymore */
|
|
MmMdFreeDescriptor(FoundDescriptor);
|
|
}
|
|
|
|
/* Return the allocation region back */
|
|
RtlCopyMemory(Descriptor, &LocalDescriptor, sizeof(LocalDescriptor));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPaAllocatePages (
|
|
_In_ PBL_MEMORY_DESCRIPTOR_LIST NewList,
|
|
_In_ PBL_MEMORY_DESCRIPTOR Descriptor,
|
|
_In_ PBL_MEMORY_DESCRIPTOR_LIST CurrentList,
|
|
_In_ PBL_PA_REQUEST Request,
|
|
_In_ BL_MEMORY_TYPE MemoryType
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Heap and page directory/table pages have a special flag */
|
|
if ((MemoryType >= BlLoaderHeap) && (MemoryType <= BlLoaderReferencePage))
|
|
{
|
|
Request->Flags |= BlMemorySpecial;
|
|
}
|
|
|
|
/* Try to find a free region of RAM matching this range and request */
|
|
Request->MemoryType = BlConventionalMemory;
|
|
Status = MmPapAllocateRegionFromMdl(NewList,
|
|
Descriptor,
|
|
CurrentList,
|
|
Request,
|
|
MemoryType);
|
|
if (Status == STATUS_NOT_FOUND)
|
|
{
|
|
/* Need to re-synchronize the memory map and check other lists */
|
|
EfiPrintf(L"No RAM found -- backup plan not yet implemented\r\n");
|
|
}
|
|
|
|
/* Did we get the region we wanted? */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* All good, return back */
|
|
return Status;
|
|
}
|
|
|
|
/* Are we failing due to some attributes? */
|
|
if (Request->Flags & BlMemoryValidAllocationAttributeMask)
|
|
{
|
|
if (Request->Flags & BlMemoryLargePages)
|
|
{
|
|
EfiPrintf(L"large alloc fail not yet implemented %lx\r\n", Status);
|
|
EfiStall(1000000);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
if (Request->Flags & BlMemoryFixed)
|
|
{
|
|
EfiPrintf(L"fixed alloc fail not yet implemented %lx\r\n", Status);
|
|
EfiStall(1000000);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
/* Nope, just fail the entire call */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapAllocatePhysicalPagesInRange (
|
|
_Inout_ PPHYSICAL_ADDRESS BaseAddress,
|
|
_In_ BL_MEMORY_TYPE MemoryType,
|
|
_In_ ULONGLONG Pages,
|
|
_In_ ULONG Attributes,
|
|
_In_ ULONG Alignment,
|
|
_In_ PBL_MEMORY_DESCRIPTOR_LIST NewList,
|
|
_In_opt_ PBL_ADDRESS_RANGE Range,
|
|
_In_ ULONG RangeType
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BL_PA_REQUEST Request;
|
|
BL_MEMORY_DESCRIPTOR Descriptor;
|
|
|
|
/* Increase nesting depth */
|
|
++MmDescriptorCallTreeCount;
|
|
|
|
/* Bail out if no address was specified */
|
|
if (!BaseAddress)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Bail out if no page count was passed in, or a bad list was specified */
|
|
if (!(Pages) ||
|
|
((NewList != &MmMdlUnmappedAllocated) &&
|
|
(NewList != &MmMdlPersistentMemory)))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Bail out if the passed in range is invalid */
|
|
if ((Range) && (Range->Minimum >= Range->Maximum))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Adjust alignment as needed */
|
|
if (!Alignment)
|
|
{
|
|
Alignment = 1;
|
|
}
|
|
|
|
/* Clear the virtual range */
|
|
Request.VirtualRange.Minimum = 0;
|
|
Request.VirtualRange.Maximum = 0;
|
|
|
|
/* Check if a fixed allocation was requested*/
|
|
if (Attributes & BlMemoryFixed)
|
|
{
|
|
/* Force the only available range to be the passed in address */
|
|
Request.BaseRange.Minimum = BaseAddress->QuadPart >> PAGE_SHIFT;
|
|
Request.BaseRange.Maximum = Request.BaseRange.Minimum + Pages - 1;
|
|
}
|
|
else if (Range)
|
|
{
|
|
/* Otherwise, a manual range was specified, use it */
|
|
Request.BaseRange.Minimum = Range->Minimum >> PAGE_SHIFT;
|
|
Request.BaseRange.Maximum = Request.BaseRange.Minimum +
|
|
(Range->Maximum >> PAGE_SHIFT) - 1;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, use any possible range of pages */
|
|
Request.BaseRange.Minimum = PapMinimumPhysicalPage;
|
|
Request.BaseRange.Maximum = MAXULONG >> PAGE_SHIFT;
|
|
}
|
|
|
|
/* Check if no type was specified, or if it was invalid */
|
|
if (!(RangeType) ||
|
|
(RangeType & ~(BL_MM_REQUEST_TOP_DOWN_TYPE | BL_MM_REQUEST_DEFAULT_TYPE)))
|
|
{
|
|
/* Use default type */
|
|
Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
|
|
}
|
|
else
|
|
{
|
|
/* Use the requested type */
|
|
Request.Type = RangeType;
|
|
}
|
|
|
|
/* Capture the other request parameters */
|
|
Request.Alignment = Alignment;
|
|
Request.Pages = Pages;
|
|
Request.Flags = Attributes;
|
|
Status = MmPaAllocatePages(NewList,
|
|
&Descriptor,
|
|
&MmMdlUnmappedUnallocated,
|
|
&Request,
|
|
MemoryType);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* We got a descriptor back, return its address */
|
|
BaseAddress->QuadPart = Descriptor.BasePage << PAGE_SHIFT;
|
|
}
|
|
|
|
Quickie:
|
|
/* Restore the nesting depth */
|
|
MmMdFreeGlobalDescriptors();
|
|
--MmDescriptorCallTreeCount;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapPageAllocatorExtend (
|
|
_In_ ULONG Attributes,
|
|
_In_ ULONG Alignment,
|
|
_In_ ULONGLONG PageCount,
|
|
_In_ ULONGLONG VirtualPage,
|
|
_In_opt_ PBL_ADDRESS_RANGE Range,
|
|
_In_opt_ ULONG Type
|
|
)
|
|
{
|
|
BL_PA_REQUEST Request;
|
|
ULONGLONG PageRange;
|
|
BL_MEMORY_DESCRIPTOR NewDescriptor;
|
|
ULONG AllocationFlags, CacheAttributes, AddFlags;
|
|
NTSTATUS Status;
|
|
PBL_MEMORY_DESCRIPTOR_LIST MdList;
|
|
PBL_MEMORY_DESCRIPTOR Descriptor;
|
|
PVOID VirtualAddress;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
|
|
/* Is the caller requesting less pages than allowed? */
|
|
if (!(Attributes & BlMemoryFixed) &&
|
|
!(Range) &&
|
|
(PageCount < PapMinimumAllocationCount))
|
|
{
|
|
/* Unless this is a fixed request, then adjust the original requirements */
|
|
PageCount = PapMinimumAllocationCount;
|
|
Alignment = PapMinimumAllocationCount;
|
|
}
|
|
|
|
/* Extract only the allocation attributes */
|
|
AllocationFlags = Attributes & BlMemoryValidAllocationAttributeMask;
|
|
|
|
/* Check if the caller wants large pages */
|
|
if ((AllocationFlags & BlMemoryLargePages) && (MmArchLargePageSize != 1))
|
|
{
|
|
EfiPrintf(L"Large pages not supported!\r\n");
|
|
EfiStall(10000000);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Set an emty virtual range */
|
|
Request.VirtualRange.Minimum = 0;
|
|
Request.VirtualRange.Maximum = 0;
|
|
|
|
/* Check if the caller requested a range */
|
|
if (Range)
|
|
{
|
|
/* Calculate it size in pages, minus a page as this is a 0-based range */
|
|
PageRange = ((Range->Maximum - Range->Minimum) >> PAGE_SHIFT) - 1;
|
|
|
|
/* Set the minimum and maximum, in pages */
|
|
Request.BaseRange.Minimum = Range->Minimum >> PAGE_SHIFT;
|
|
Request.BaseRange.Maximum = Request.BaseRange.Minimum + PageRange;
|
|
}
|
|
else
|
|
{
|
|
/* Initialize a range from the smallest page to the biggest */
|
|
Request.BaseRange.Minimum = PapMinimumPhysicalPage;
|
|
Request.BaseRange.Maximum = 0xFFFFFFFF / PAGE_SIZE;
|
|
}
|
|
|
|
/* Get the cache attributes */
|
|
CacheAttributes = Attributes & BlMemoryValidCacheAttributeMask;
|
|
|
|
/* Check if the caller requested a valid allocation type */
|
|
if ((Type) && !(Type & ~(BL_MM_REQUEST_DEFAULT_TYPE |
|
|
BL_MM_REQUEST_TOP_DOWN_TYPE)))
|
|
{
|
|
/* Use what the caller wanted */
|
|
Request.Type = Type;
|
|
}
|
|
else
|
|
{
|
|
/* Use the default bottom-up type */
|
|
Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
|
|
}
|
|
|
|
/* Use the original protection and type, but ignore other attributes */
|
|
Request.Flags = Attributes & ~(BlMemoryValidAllocationAttributeMask |
|
|
BlMemoryValidCacheAttributeMask);
|
|
Request.Alignment = Alignment;
|
|
Request.Pages = PageCount;
|
|
|
|
/* Allocate some free pages */
|
|
Status = MmPaAllocatePages(NULL,
|
|
&NewDescriptor,
|
|
&MmMdlUnmappedUnallocated,
|
|
&Request,
|
|
BlConventionalMemory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EfiPrintf(L"Failed to get unmapped, unallocated memory!\r\n");
|
|
EfiStall(10000000);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize a descriptor for these pages, adding in the allocation flags */
|
|
Descriptor = MmMdInitByteGranularDescriptor(AllocationFlags |
|
|
NewDescriptor.Flags,
|
|
BlConventionalMemory,
|
|
NewDescriptor.BasePage,
|
|
NewDescriptor.VirtualPage,
|
|
NewDescriptor.PageCount);
|
|
|
|
/* Now map a virtual address for these physical pages */
|
|
VirtualAddress = (PVOID)((ULONG_PTR)VirtualPage << PAGE_SHIFT);
|
|
PhysicalAddress.QuadPart = NewDescriptor.BasePage << PAGE_SHIFT;
|
|
Status = BlMmMapPhysicalAddressEx(&VirtualAddress,
|
|
AllocationFlags | CacheAttributes,
|
|
NewDescriptor.PageCount << PAGE_SHIFT,
|
|
PhysicalAddress);
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
/* Add the cache attributes now that the mapping worked */
|
|
Descriptor->Flags |= CacheAttributes;
|
|
|
|
/* Update the virtual page now that we mapped it */
|
|
Descriptor->VirtualPage = (ULONG_PTR)VirtualAddress >> PAGE_SHIFT;
|
|
|
|
/* Add this as a mapped region */
|
|
Status = MmMdAddDescriptorToList(&MmMdlMappedUnallocated,
|
|
Descriptor,
|
|
BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG);
|
|
|
|
/* Make new descriptor that we'll add in firmware allocation tracker */
|
|
MdList = &MmMdlFwAllocationTracker;
|
|
Descriptor = MmMdInitByteGranularDescriptor(0,
|
|
BlConventionalMemory,
|
|
NewDescriptor.BasePage,
|
|
0,
|
|
NewDescriptor.PageCount);
|
|
|
|
/* Do not coalesce */
|
|
AddFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
/* We failed, free the physical pages */
|
|
Status = MmFwFreePages(NewDescriptor.BasePage, NewDescriptor.PageCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* We failed to free the pages, so this is still around */
|
|
MdList = &MmMdlUnmappedAllocated;
|
|
}
|
|
else
|
|
{
|
|
/* This is now back to unmapped/unallocated memory */
|
|
Descriptor->Flags = 0;
|
|
MdList = &MmMdlUnmappedUnallocated;
|
|
}
|
|
|
|
/* Coalesce the free descriptor */
|
|
AddFlags = BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG;
|
|
}
|
|
|
|
/* Either add to firmware list, or to unmapped list, then return result */
|
|
MmMdAddDescriptorToList(MdList, Descriptor, AddFlags);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapAllocatePagesInRange (
|
|
_Inout_ PVOID* PhysicalAddress,
|
|
_In_ BL_MEMORY_TYPE MemoryType,
|
|
_In_ ULONGLONG Pages,
|
|
_In_ ULONG Attributes,
|
|
_In_ ULONG Alignment,
|
|
_In_opt_ PBL_ADDRESS_RANGE Range,
|
|
_In_ ULONG Type
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PHYSICAL_ADDRESS BaseAddress;
|
|
BL_PA_REQUEST Request;
|
|
PBL_MEMORY_DESCRIPTOR_LIST List;
|
|
BL_MEMORY_DESCRIPTOR Descriptor;
|
|
|
|
/* Increment nesting depth */
|
|
++MmDescriptorCallTreeCount;
|
|
|
|
/* Default list */
|
|
List = &MmMdlMappedAllocated;
|
|
|
|
/* Check for missing parameters or invalid range */
|
|
if (!(PhysicalAddress) ||
|
|
!(Pages) ||
|
|
((Range) && (Range->Minimum >= Range->Maximum)))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
/* What translation mode are we using? */
|
|
if (MmTranslationType != BlNone)
|
|
{
|
|
/* Use 1 page alignment if none was requested */
|
|
if (!Alignment)
|
|
{
|
|
Alignment = 1;
|
|
}
|
|
|
|
/* Check if we got a range */
|
|
if (Range)
|
|
{
|
|
/* We don't support virtual memory yet @TODO */
|
|
EfiPrintf(L"virt range not yet implemented in %S\r\n", __FUNCTION__);
|
|
EfiStall(1000000);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
/* Use the entire range that's possible */
|
|
Request.BaseRange.Minimum = PapMinimumPhysicalPage;
|
|
Request.BaseRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
|
|
}
|
|
|
|
/* Check if a fixed allocation was requested */
|
|
if (Attributes & BlMemoryFixed)
|
|
{
|
|
/* We don't support virtual memory yet @TODO */
|
|
EfiPrintf(L"fixed not yet implemented in %S\r\n", __FUNCTION__);
|
|
EfiStall(1000000);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
/* Check if kernel range was specifically requested */
|
|
if (Attributes & BlMemoryKernelRange)
|
|
{
|
|
/* Use the kernel range */
|
|
Request.VirtualRange.Minimum = MmArchKsegAddressRange.Minimum >> PAGE_SHIFT;
|
|
Request.VirtualRange.Maximum = MmArchKsegAddressRange.Maximum >> PAGE_SHIFT;
|
|
}
|
|
else
|
|
{
|
|
/* Set the virtual address range */
|
|
Request.VirtualRange.Minimum = 0;
|
|
Request.VirtualRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
|
|
}
|
|
}
|
|
|
|
/* Check what type of allocation was requested */
|
|
if ((Type) && !(Type & ~(BL_MM_REQUEST_DEFAULT_TYPE |
|
|
BL_MM_REQUEST_TOP_DOWN_TYPE)))
|
|
{
|
|
/* Save it if it was valid */
|
|
Request.Type = Type;
|
|
}
|
|
else
|
|
{
|
|
/* Set the default */
|
|
Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
|
|
}
|
|
|
|
/* Fill out the request of the request */
|
|
Request.Flags = Attributes;
|
|
Request.Alignment = Alignment;
|
|
Request.Pages = Pages;
|
|
|
|
/* Try to allocate the pages */
|
|
Status = MmPaAllocatePages(List,
|
|
&Descriptor,
|
|
&MmMdlMappedUnallocated,
|
|
&Request,
|
|
MemoryType);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Extend the physical allocator */
|
|
Status = MmPapPageAllocatorExtend(Attributes,
|
|
Alignment,
|
|
Pages,
|
|
((ULONG_PTR)*PhysicalAddress) >>
|
|
PAGE_SHIFT,
|
|
Range,
|
|
Type);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail since we're out of memory */
|
|
EfiPrintf(L"EXTEND OUT OF MEMORY: %lx\r\n", Status);
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
/* Try the allocation again now */
|
|
Status = MmPaAllocatePages(&MmMdlMappedAllocated,
|
|
&Descriptor,
|
|
&MmMdlMappedUnallocated,
|
|
&Request,
|
|
MemoryType);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail since we're out of memory */
|
|
EfiPrintf(L"PALLOC OUT OF MEMORY: %lx\r\n", Status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/* Return the allocated address */
|
|
*PhysicalAddress = (PVOID)((ULONG_PTR)Descriptor.VirtualPage << PAGE_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
/* Check if this is a fixed allocation */
|
|
BaseAddress.QuadPart = (Attributes & BlMemoryFixed) ?
|
|
(ULONG_PTR)*PhysicalAddress : 0;
|
|
|
|
/* Allocate the pages */
|
|
Status = MmPapAllocatePhysicalPagesInRange(&BaseAddress,
|
|
MemoryType,
|
|
Pages,
|
|
Attributes,
|
|
Alignment,
|
|
(&MmMdlMappedAllocated !=
|
|
&MmMdlPersistentMemory) ?
|
|
&MmMdlUnmappedAllocated :
|
|
&MmMdlMappedAllocated,
|
|
Range,
|
|
Type);
|
|
|
|
/* Return the allocated address */
|
|
*PhysicalAddress = PhysicalAddressToPtr(BaseAddress);
|
|
}
|
|
|
|
Exit:
|
|
/* Restore the nesting depth */
|
|
MmMdFreeGlobalDescriptors();
|
|
--MmDescriptorCallTreeCount;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPaInitialize (
|
|
__in PBL_MEMORY_DATA BootMemoryData,
|
|
__in ULONG MinimumAllocationCount
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ExistingDescriptors, FinalOffset;
|
|
PBL_MEMORY_DESCRIPTOR Descriptor, NewDescriptor;
|
|
|
|
/* Initialize physical allocator variables */
|
|
PapMaximumPhysicalPage = 0xFFFFFFFFFFFFF;
|
|
PapMinimumAllocationCount = MinimumAllocationCount;
|
|
PapMinimumPhysicalPage = 0;
|
|
|
|
/* Initialize all the lists */
|
|
MmMdInitializeListHead(&MmMdlMappedAllocated);
|
|
MmMdInitializeListHead(&MmMdlMappedUnallocated);
|
|
MmMdInitializeListHead(&MmMdlFwAllocationTracker);
|
|
MmMdInitializeListHead(&MmMdlUnmappedAllocated);
|
|
MmMdInitializeListHead(&MmMdlReservedAllocated);
|
|
MmMdInitializeListHead(&MmMdlBadMemory);
|
|
MmMdInitializeListHead(&MmMdlTruncatedMemory);
|
|
MmMdInitializeListHead(&MmMdlPersistentMemory);
|
|
MmMdInitializeListHead(&MmMdlUnmappedUnallocated);
|
|
MmMdInitializeListHead(&MmMdlCompleteBadMemory);
|
|
|
|
/* Get the BIOS memory map */
|
|
Status = MmFwGetMemoryMap(&MmMdlUnmappedUnallocated,
|
|
BL_MM_FLAG_USE_FIRMWARE_FOR_MEMORY_MAP_BUFFERS |
|
|
BL_MM_FLAG_REQUEST_COALESCING);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
#if 0
|
|
PLIST_ENTRY listHead, nextEntry;
|
|
|
|
/* Loop the NT firmware memory list */
|
|
EfiPrintf(L"NT MEMORY MAP\n\r\n");
|
|
listHead = &MmMdlUnmappedUnallocated.ListHead;
|
|
nextEntry = listHead->Flink;
|
|
while (listHead != nextEntry)
|
|
{
|
|
Descriptor = CONTAINING_RECORD(nextEntry, BL_MEMORY_DESCRIPTOR, ListEntry);
|
|
|
|
EfiPrintf(L"Type: %08lX Flags: %08lX Base: 0x%016I64X End: 0x%016I64X\r\n",
|
|
Descriptor->Type,
|
|
Descriptor->Flags,
|
|
Descriptor->BasePage << PAGE_SHIFT,
|
|
(Descriptor->BasePage + Descriptor->PageCount) << PAGE_SHIFT);
|
|
|
|
nextEntry = nextEntry->Flink;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Because BL supports cross x86-x64 application launches and a LIST_ENTRY
|
|
* is of variable size, care must be taken here to ensure that we see a
|
|
* consistent view of descriptors. BL uses some offset magic to figure out
|
|
* where the data actually starts, since everything is ULONGLONG past the
|
|
* LIST_ENTRY itself
|
|
*/
|
|
FinalOffset = BootMemoryData->MdListOffset + BootMemoryData->DescriptorOffset;
|
|
Descriptor = (PBL_MEMORY_DESCRIPTOR)((ULONG_PTR)BootMemoryData + FinalOffset -
|
|
FIELD_OFFSET(BL_MEMORY_DESCRIPTOR, BasePage));
|
|
|
|
/* Scan all of them */
|
|
ExistingDescriptors = BootMemoryData->DescriptorCount;
|
|
while (ExistingDescriptors != 0)
|
|
{
|
|
/* Remove this region from our free memory MDL */
|
|
//EfiPrintf(L"Handling existing descriptor: %llx %llx\r\n", Descriptor->BasePage, Descriptor->PageCount);
|
|
Status = MmMdRemoveRegionFromMdlEx(&MmMdlUnmappedUnallocated,
|
|
BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
|
|
Descriptor->BasePage,
|
|
Descriptor->PageCount,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Build a descriptor for it */
|
|
NewDescriptor = MmMdInitByteGranularDescriptor(Descriptor->Flags,
|
|
Descriptor->Type,
|
|
Descriptor->BasePage,
|
|
Descriptor->VirtualPage,
|
|
Descriptor->PageCount);
|
|
if (!NewDescriptor)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* And add this region to the reserved & allocated MDL */
|
|
Status = MmMdAddDescriptorToList(&MmMdlReservedAllocated, NewDescriptor, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Move on to the next descriptor */
|
|
ExistingDescriptors--;
|
|
Descriptor = (PBL_MEMORY_DESCRIPTOR)((ULONG_PTR)Descriptor + BootMemoryData->DescriptorSize);
|
|
}
|
|
|
|
/* We are done, so check for any RAM constraints which will make us truncate memory */
|
|
Status = BlpMmInitializeConstraints();
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* The Page Allocator has initialized */
|
|
PapInitializationStatus = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
BlMmAllocatePhysicalPages(
|
|
_In_ PPHYSICAL_ADDRESS Address,
|
|
_In_ BL_MEMORY_TYPE MemoryType,
|
|
_In_ ULONGLONG PageCount,
|
|
_In_ ULONG Attributes,
|
|
_In_ ULONG Alignment
|
|
)
|
|
{
|
|
/* Call the physical allocator */
|
|
return MmPapAllocatePhysicalPagesInRange(Address,
|
|
MemoryType,
|
|
PageCount,
|
|
Attributes,
|
|
Alignment,
|
|
&MmMdlUnmappedAllocated,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapFreePhysicalPages (
|
|
_In_ ULONG WhichList,
|
|
_In_ ULONGLONG PageCount,
|
|
_In_ PHYSICAL_ADDRESS Address
|
|
)
|
|
{
|
|
PBL_MEMORY_DESCRIPTOR Descriptor;
|
|
ULONGLONG Page;
|
|
ULONG DescriptorFlags, Flags;
|
|
BOOLEAN DontFree, HasPageData;
|
|
BL_LIBRARY_PARAMETERS LibraryParameters;
|
|
NTSTATUS Status;
|
|
|
|
/* Set some defaults */
|
|
Flags = BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG;
|
|
DontFree = FALSE;
|
|
HasPageData = FALSE;
|
|
|
|
/* Only page-aligned addresses are accepted */
|
|
if (Address.QuadPart & (PAGE_SIZE - 1))
|
|
{
|
|
EfiPrintf(L"free mem fail 1\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Try to find the descriptor containing this address */
|
|
Page = Address.QuadPart >> PAGE_SHIFT;
|
|
Descriptor = MmMdFindDescriptor(WhichList,
|
|
BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
|
|
Page);
|
|
if (!Descriptor)
|
|
{
|
|
EfiPrintf(L"free mem fail 2\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* If a page count was given, it must match, unless it's coalesced */
|
|
DescriptorFlags = Descriptor->Flags;
|
|
if (!(DescriptorFlags & BlMemoryCoalesced) &&
|
|
(PageCount) && (PageCount != Descriptor->PageCount))
|
|
{
|
|
EfiPrintf(L"free mem fail 3\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if this is persistent memory in teardown status */
|
|
if ((PapInitializationStatus == 2) &&
|
|
(DescriptorFlags & BlMemoryPersistent))
|
|
{
|
|
/* Then we should keep it */
|
|
DontFree = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Mark it as non-persistent, since we're freeing it */
|
|
Descriptor->Flags &= ~BlMemoryPersistent;
|
|
}
|
|
|
|
/* Check if this memory contains paging data */
|
|
if ((Descriptor->Type == BlLoaderPageDirectory) ||
|
|
(Descriptor->Type == BlLoaderReferencePage))
|
|
{
|
|
HasPageData = TRUE;
|
|
}
|
|
|
|
/* Check if a page count was given */
|
|
if (PageCount)
|
|
{
|
|
/* The pages must fit within the descriptor */
|
|
if ((PageCount + Page - Descriptor->BasePage) > Descriptor->PageCount)
|
|
{
|
|
EfiPrintf(L"free mem fail 4\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No page count given, so the address must be at the beginning then */
|
|
if (Descriptor->BasePage != Page)
|
|
{
|
|
EfiPrintf(L"free mem fail 5\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* And we'll use the page count in the descriptor */
|
|
PageCount = Descriptor->PageCount;
|
|
}
|
|
|
|
/* Copy library parameters since we will read them */
|
|
RtlCopyMemory(&LibraryParameters,
|
|
&BlpLibraryParameters,
|
|
sizeof(LibraryParameters));
|
|
|
|
/* Check if this is teardown */
|
|
if (PapInitializationStatus == 2)
|
|
{
|
|
EfiPrintf(L"Case 2 not yet handled!\r\n");
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
else if (!DontFree)
|
|
{
|
|
/* Caller wants memory to be freed -- should we zero it? */
|
|
if (!(HasPageData) &&
|
|
(LibraryParameters.LibraryFlags &
|
|
BL_LIBRARY_FLAG_ZERO_HEAP_ALLOCATIONS_ON_FREE))
|
|
{
|
|
EfiPrintf(L"Freeing zero data not yet handled!\r\n");
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
/* Now call into firmware to actually free the physical pages */
|
|
Status = MmFwFreePages(Page, PageCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EfiPrintf(L"free mem fail 6\r\n");
|
|
return Status;
|
|
}
|
|
|
|
/* Remove the firmware flags */
|
|
Descriptor->Flags &= ~(BlMemoryNonFirmware |
|
|
BlMemoryFirmware |
|
|
BlMemoryPersistent);
|
|
|
|
/* If we're not actually freeing, don't coalesce with anyone nearby */
|
|
if (DontFree)
|
|
{
|
|
Flags |= BL_MM_ADD_DESCRIPTOR_NEVER_COALESCE_FLAG;
|
|
}
|
|
|
|
/* Check if the entire allocation is being freed */
|
|
if (PageCount == Descriptor->PageCount)
|
|
{
|
|
/* Remove the descriptor from the allocated list */
|
|
MmMdRemoveDescriptorFromList(&MmMdlUnmappedAllocated, Descriptor);
|
|
|
|
/* Mark the entire descriptor as free */
|
|
Descriptor->Type = BlConventionalMemory;
|
|
}
|
|
else
|
|
{
|
|
/* Init a descriptor for what we're actually freeing */
|
|
Descriptor = MmMdInitByteGranularDescriptor(Descriptor->Flags,
|
|
BlConventionalMemory,
|
|
Page,
|
|
0,
|
|
PageCount);
|
|
if (!Descriptor)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Remove the region from the existing descriptor */
|
|
Status = MmMdRemoveRegionFromMdlEx(&MmMdlUnmappedAllocated,
|
|
BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
|
|
Page,
|
|
PageCount,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Add the new descriptor into in the list (or the old, repurposed one) */
|
|
Descriptor->Flags &= ~BlMemoryCoalesced;
|
|
return MmMdAddDescriptorToList(&MmMdlUnmappedUnallocated, Descriptor, Flags);
|
|
}
|
|
|
|
NTSTATUS
|
|
BlMmFreePhysicalPages (
|
|
_In_ PHYSICAL_ADDRESS Address
|
|
)
|
|
{
|
|
/* Call the physical allocator */
|
|
return MmPapFreePhysicalPages(BL_MM_INCLUDE_UNMAPPED_ALLOCATED, 0, Address);
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPapFreePages (
|
|
_In_ PVOID Address,
|
|
_In_ ULONG WhichList
|
|
)
|
|
{
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
|
|
/* Handle virtual memory scenario */
|
|
if (MmTranslationType != BlNone)
|
|
{
|
|
EfiPrintf(L"Unimplemented free virtual path: %p %lx\r\n", Address, WhichList);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Physical memory should be in the unmapped allocated list */
|
|
if (WhichList != BL_MM_INCLUDE_PERSISTENT_MEMORY)
|
|
{
|
|
WhichList = BL_MM_INCLUDE_UNMAPPED_ALLOCATED;
|
|
}
|
|
|
|
/* Free it from there */
|
|
PhysicalAddress.QuadPart = (ULONG_PTR)Address;
|
|
return MmPapFreePhysicalPages(WhichList, 0, PhysicalAddress);
|
|
}
|
|
|
|
NTSTATUS
|
|
BlMmGetMemoryMap (
|
|
_In_ PLIST_ENTRY MemoryMap,
|
|
_In_ PBL_BUFFER_DESCRIPTOR MemoryParameters,
|
|
_In_ ULONG WhichTypes,
|
|
_In_ ULONG Flags
|
|
)
|
|
{
|
|
BL_MEMORY_DESCRIPTOR_LIST FirmwareMdList, FullMdList;
|
|
BOOLEAN DoFirmware, DoPersistent, DoTruncated, DoBad;
|
|
BOOLEAN DoReserved, DoUnmapUnalloc, DoUnmapAlloc;
|
|
BOOLEAN DoMapAlloc, DoMapUnalloc, DoFirmware2;
|
|
ULONG LoopCount, MdListCount, MdListSize, Used;
|
|
NTSTATUS Status;
|
|
|
|
/* Initialize the firmware list if we use it */
|
|
MmMdInitializeListHead(&FirmwareMdList);
|
|
|
|
/* Make sure we got our input parameters */
|
|
if (!(MemoryMap) || !(MemoryParameters))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Either ask for firmware memory, or don't. Not neither */
|
|
if ((WhichTypes & ~BL_MM_INCLUDE_NO_FIRMWARE_MEMORY) &&
|
|
(WhichTypes & ~BL_MM_INCLUDE_ONLY_FIRMWARE_MEMORY))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Either ask for firmware memory, or don't. Not both */
|
|
if ((WhichTypes & BL_MM_INCLUDE_NO_FIRMWARE_MEMORY) &&
|
|
(WhichTypes & BL_MM_INCLUDE_ONLY_FIRMWARE_MEMORY))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Initialize the memory map list */
|
|
InitializeListHead(MemoryMap);
|
|
|
|
/* Check which types of memory to dump */
|
|
DoFirmware = WhichTypes & BL_MM_INCLUDE_FIRMWARE_MEMORY;
|
|
DoPersistent = WhichTypes & BL_MM_INCLUDE_PERSISTENT_MEMORY;
|
|
DoTruncated = WhichTypes & BL_MM_INCLUDE_TRUNCATED_MEMORY;
|
|
DoBad = WhichTypes & BL_MM_INCLUDE_BAD_MEMORY;
|
|
DoReserved = WhichTypes & BL_MM_INCLUDE_RESERVED_ALLOCATED;
|
|
DoUnmapUnalloc = WhichTypes & BL_MM_INCLUDE_UNMAPPED_UNALLOCATED;
|
|
DoUnmapAlloc = WhichTypes & BL_MM_INCLUDE_UNMAPPED_ALLOCATED;
|
|
DoMapAlloc = WhichTypes & BL_MM_INCLUDE_MAPPED_ALLOCATED;
|
|
DoMapUnalloc = WhichTypes & BL_MM_INCLUDE_MAPPED_UNALLOCATED;
|
|
DoFirmware2 = WhichTypes & BL_MM_INCLUDE_FIRMWARE_MEMORY_2;
|
|
|
|
/* Begin the attempt loop */
|
|
LoopCount = 0;
|
|
while (TRUE)
|
|
{
|
|
/* Count how many entries we will need */
|
|
MdListCount = 0;
|
|
if (DoMapAlloc) MdListCount = MmMdCountList(&MmMdlMappedAllocated);
|
|
if (DoMapUnalloc) MdListCount += MmMdCountList(&MmMdlMappedUnallocated);
|
|
if (DoUnmapAlloc) MdListCount += MmMdCountList(&MmMdlUnmappedAllocated);
|
|
if (DoUnmapUnalloc) MdListCount += MmMdCountList(&MmMdlUnmappedUnallocated);
|
|
if (DoReserved) MdListCount += MmMdCountList(&MmMdlReservedAllocated);
|
|
if (DoBad) MdListCount += MmMdCountList(&MmMdlBadMemory);
|
|
if (DoTruncated) MdListCount += MmMdCountList(&MmMdlTruncatedMemory);
|
|
if (DoPersistent) MdListCount += MmMdCountList(&MmMdlPersistentMemory);
|
|
|
|
/* Plus firmware entries */
|
|
if (DoFirmware)
|
|
{
|
|
/* Free the previous entries, if any */
|
|
MmMdFreeList(&FirmwareMdList);
|
|
|
|
/* Get the firmware map, coalesced */
|
|
Status = MmFwGetMemoryMap(&FirmwareMdList,
|
|
BL_MM_FLAG_REQUEST_COALESCING);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Quickie;
|
|
}
|
|
|
|
/* We overwrite, since this type is exclusive */
|
|
MdListCount = MmMdCountList(&FirmwareMdList);
|
|
}
|
|
|
|
/* Plus firmware entries-2 */
|
|
if (DoFirmware2)
|
|
{
|
|
/* Free the previous entries, if any */
|
|
MmMdFreeList(&FirmwareMdList);
|
|
|
|
/* Get the firmware map, uncoalesced */
|
|
Status = MmFwGetMemoryMap(&FirmwareMdList, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Quickie;
|
|
}
|
|
|
|
/* We overwrite, since this type is exclusive */
|
|
MdListCount = MmMdCountList(&FirmwareMdList);
|
|
}
|
|
|
|
/* If there's no descriptors, we're done */
|
|
if (!MdListCount)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Check if the buffer we have is big enough */
|
|
if (MemoryParameters->BufferSize >=
|
|
(sizeof(BL_MEMORY_DESCRIPTOR) * MdListCount))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* It's not, allocate it, with a slack of 4 extra descriptors */
|
|
MdListSize = sizeof(BL_MEMORY_DESCRIPTOR) * (MdListCount + 4);
|
|
|
|
/* Except if we weren't asked to */
|
|
if (!(Flags & BL_MM_ADD_DESCRIPTOR_ALLOCATE_FLAG))
|
|
{
|
|
MemoryParameters->BufferSize = MdListSize;
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Has it been less than 4 times we've tried this? */
|
|
if (++LoopCount <= 4)
|
|
{
|
|
/* Free the previous attempt, if any */
|
|
if (MemoryParameters->BufferSize)
|
|
{
|
|
BlMmFreeHeap(MemoryParameters->Buffer);
|
|
}
|
|
|
|
/* Allocate a new buffer */
|
|
MemoryParameters->BufferSize = MdListSize;
|
|
MemoryParameters->Buffer = BlMmAllocateHeap(MdListSize);
|
|
if (MemoryParameters->Buffer)
|
|
{
|
|
/* Try again */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* If we got here, we're out of memory after 4 attempts */
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* We should have a buffer by now... */
|
|
if (MemoryParameters->Buffer)
|
|
{
|
|
/* Zero it out */
|
|
RtlZeroMemory(MemoryParameters->Buffer,
|
|
MdListCount * sizeof(BL_MEMORY_DESCRIPTOR));
|
|
}
|
|
|
|
/* Initialize our list of descriptors */
|
|
MmMdInitializeList(&FullMdList, 0, MemoryMap);
|
|
Used = 0;
|
|
|
|
/* Handle mapped, allocated */
|
|
if (DoMapAlloc)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlMappedAllocated,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle mapped, unallocated */
|
|
if (DoMapUnalloc)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlMappedUnallocated,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle unmapped, allocated */
|
|
if (DoUnmapAlloc)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlUnmappedAllocated,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle unmapped, unallocated */
|
|
if (DoUnmapUnalloc)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlUnmappedUnallocated,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle reserved, allocated */
|
|
if (DoReserved)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlReservedAllocated,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle bad */
|
|
if (DoBad)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlBadMemory,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle truncated */
|
|
if (DoTruncated)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlTruncatedMemory,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle persistent */
|
|
if (DoPersistent)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&MmMdlPersistentMemory,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle firmware */
|
|
if (DoFirmware)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&FirmwareMdList,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Handle firmware2 */
|
|
if (DoFirmware2)
|
|
{
|
|
Status = MmMdCopyList(&FullMdList,
|
|
&FirmwareMdList,
|
|
MemoryParameters->Buffer,
|
|
&Used,
|
|
MdListCount,
|
|
Flags);
|
|
}
|
|
|
|
/* Add up the final size */
|
|
Status = RtlULongLongToULong(Used * sizeof(BL_MEMORY_DESCRIPTOR),
|
|
&MemoryParameters->ActualSize);
|
|
|
|
Quickie:
|
|
MmMdFreeList(&FirmwareMdList);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPaReleaseSelfMapPages (
|
|
_In_ PHYSICAL_ADDRESS Address
|
|
)
|
|
{
|
|
PBL_MEMORY_DESCRIPTOR Descriptor;
|
|
ULONGLONG BasePage;
|
|
NTSTATUS Status;
|
|
|
|
/* Only page-aligned addresses are accepted */
|
|
if (Address.QuadPart & (PAGE_SIZE - 1))
|
|
{
|
|
EfiPrintf(L"free mem fail 1\r\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Get the base page, and find a descriptor that matches */
|
|
BasePage = Address.QuadPart >> PAGE_SHIFT;
|
|
Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_UNMAPPED_UNALLOCATED,
|
|
BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
|
|
BasePage);
|
|
if (!(Descriptor) || (Descriptor->BasePage != BasePage))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Free the physical pages */
|
|
Status = MmFwFreePages(BasePage, Descriptor->PageCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Remove the firmware flags */
|
|
Descriptor->Flags &= ~(BlMemoryNonFirmware |
|
|
BlMemoryFirmware |
|
|
BlMemoryPersistent);
|
|
|
|
/* Set it as free memory */
|
|
Descriptor->Type = BlConventionalMemory;
|
|
|
|
/* Create a new descriptor that's free memory, covering the old range */
|
|
Descriptor = MmMdInitByteGranularDescriptor(0,
|
|
BlConventionalMemory,
|
|
BasePage,
|
|
0,
|
|
Descriptor->PageCount);
|
|
if (!Descriptor)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Insert it into the virtual free list */
|
|
return MmMdAddDescriptorToList(&MmMdlFreeVirtual,
|
|
Descriptor,
|
|
BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG |
|
|
BL_MM_ADD_DESCRIPTOR_TRUNCATE_FLAG);
|
|
}
|
|
|
|
NTSTATUS
|
|
MmPaReserveSelfMapPages (
|
|
_Inout_ PPHYSICAL_ADDRESS PhysicalAddress,
|
|
_In_ ULONG Alignment,
|
|
_In_ ULONG PageCount
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BL_PA_REQUEST Request;
|
|
BL_MEMORY_DESCRIPTOR Descriptor;
|
|
|
|
/* Increment descriptor usage count */
|
|
++MmDescriptorCallTreeCount;
|
|
|
|
/* Bail if we don't have an address */
|
|
if (!PhysicalAddress)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Make a request for the required number of self-map pages */
|
|
Request.BaseRange.Minimum = PapMinimumPhysicalPage;
|
|
Request.BaseRange.Maximum = 0xFFFFFFFF >> PAGE_SHIFT;
|
|
Request.VirtualRange.Minimum = 0;
|
|
Request.VirtualRange.Maximum = 0;
|
|
Request.Pages = PageCount;
|
|
Request.Alignment = Alignment;
|
|
Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
|
|
Request.Flags = 0;;
|
|
Status = MmPaAllocatePages(&MmMdlUnmappedUnallocated,
|
|
&Descriptor,
|
|
&MmMdlUnmappedUnallocated,
|
|
&Request,
|
|
BlLoaderSelfMap);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Remove this region from free virtual memory */
|
|
Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
|
|
BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
|
|
Descriptor.BasePage,
|
|
Descriptor.PageCount,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Return the physical address */
|
|
PhysicalAddress->QuadPart = Descriptor.BasePage << PAGE_SHIFT;
|
|
|
|
Quickie:
|
|
/* Free global descriptors and reduce the count by one */
|
|
MmMdFreeGlobalDescriptors();
|
|
--MmDescriptorCallTreeCount;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MmSelectMappingAddress (
|
|
_Out_ PVOID* MappingAddress,
|
|
_In_ PVOID PreferredAddress,
|
|
_In_ ULONGLONG Size,
|
|
_In_ ULONG AllocationAttributes,
|
|
_In_ ULONG Flags,
|
|
_In_ PHYSICAL_ADDRESS PhysicalAddress
|
|
)
|
|
{
|
|
BL_PA_REQUEST Request;
|
|
NTSTATUS Status;
|
|
BL_MEMORY_DESCRIPTOR NewDescriptor;
|
|
|
|
/* Are we in physical mode? */
|
|
if (MmTranslationType == BlNone)
|
|
{
|
|
/* Just return the physical address as the mapping address */
|
|
PreferredAddress = PhysicalAddressToPtr(PhysicalAddress);
|
|
goto Success;
|
|
}
|
|
|
|
/* If no physical address, or caller wants a fixed address... */
|
|
if ((PhysicalAddress.QuadPart == -1) || (Flags & BlMemoryFixed))
|
|
{
|
|
/* Then just return the preferred address */
|
|
goto Success;
|
|
}
|
|
|
|
/* Check which range of virtual memory should be used */
|
|
if (AllocationAttributes & BlMemoryKernelRange)
|
|
{
|
|
/* Use kernel range */
|
|
Request.BaseRange.Minimum = MmArchKsegAddressRange.Minimum >> PAGE_SHIFT;
|
|
Request.BaseRange.Maximum = MmArchKsegAddressRange.Maximum >> PAGE_SHIFT;
|
|
Request.Type = BL_MM_REQUEST_DEFAULT_TYPE;
|
|
}
|
|
else
|
|
{
|
|
/* User user/application range */
|
|
Request.BaseRange.Minimum = 0 >> PAGE_SHIFT;
|
|
Request.BaseRange.Maximum = MmArchTopOfApplicationAddressSpace >> PAGE_SHIFT;
|
|
Request.Type = BL_MM_REQUEST_TOP_DOWN_TYPE;
|
|
}
|
|
|
|
/* Build a request */
|
|
Request.VirtualRange.Minimum = 0;
|
|
Request.VirtualRange.Maximum = 0;
|
|
Request.Flags = AllocationAttributes & BlMemoryLargePages;
|
|
Request.Alignment = 1;
|
|
Request.Pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(PhysicalAddress.LowPart, Size);
|
|
|
|
/* Allocate the physical pages */
|
|
Status = MmPaAllocatePages(NULL,
|
|
&NewDescriptor,
|
|
&MmMdlFreeVirtual,
|
|
&Request,
|
|
BlConventionalMemory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Return the address we got back */
|
|
PreferredAddress = (PVOID)((ULONG_PTR)NewDescriptor.BasePage << PAGE_SHIFT);
|
|
|
|
/* Check if the existing physical address was not aligned */
|
|
if (PhysicalAddress.QuadPart != -1)
|
|
{
|
|
/* Add the offset to the returned virtual address */
|
|
PreferredAddress = (PVOID)((ULONG_PTR)PreferredAddress +
|
|
BYTE_OFFSET(PhysicalAddress.QuadPart));
|
|
}
|
|
|
|
Success:
|
|
/* Return the mapping address and success */
|
|
*MappingAddress = PreferredAddress;
|
|
return STATUS_SUCCESS;
|
|
}
|