alcyone/BOOT/ENVIRON/LIB/EFI/efimm.c
Kaimakan71 2472e39635 [BOOT:LIB] More initialization and cleanup
Started BlpMmDestroy(), MmMdDestroy(), MmPaDestroy(),
EfiSetWatchdogTimer(), EfiOpenProtocol(), EfiConInExSetState(), and
BlDestroyLibrary().
Completed BlpFwInitialize().
Improved InitializeLibrary().
2024-10-06 13:50:21 -04:00

512 lines
14 KiB
C

/*++
Copyright (c) 2024, Quinn Stephens.
Provided under the BSD 3-Clause license.
Module Name:
efimm.c
Abstract:
Provides EFI memory manager routines.
--*/
#include "bootlib.h"
#include "efi.h"
#include "efilib.h"
#include "mm.h"
#define _1MiB 1048576
#define EFI_PAGE(NtPage) (((NtPage) << PAGE_SHIFT) >> EFI_PAGE_SHIFT)
#define NT_PAGE(EfiPage) (((EfiPage) << EFI_PAGE_SHIFT) >> PAGE_SHIFT)
MEMORY_TYPE
BlMmTranslateEfiMemoryType (
IN EFI_MEMORY_TYPE EfiMemoryType
)
/*++
Routine Description:
Translates an EFI memory type to an NT memory type.
Arguments:
EfiMemoryType - the EFI memory type.
Return Value:
The NT memory type.
--*/
{
switch (EfiMemoryType) {
case EfiConventionalMemory:
return MEMORY_TYPE_FREE;
case EfiLoaderCode:
case EfiLoaderData:
return MEMORY_TYPE_BOOT_APPLICATION;
case EfiBootServicesCode:
case EfiBootServicesData:
return MEMORY_TYPE_BOOT_SERVICES;
case EfiRuntimeServicesCode:
return MEMORY_TYPE_RUNTIME_SERVICES_CODE;
case EfiRuntimeServicesData:
return MEMORY_TYPE_RUNTIME_SERVICES_DATA;
case EfiUnusableMemory:
return MEMORY_TYPE_UNUSABLE;
case EfiACPIReclaimMemory:
return MEMORY_TYPE_ACPI_RECLAIM;
case EfiACPIMemoryNVS:
return MEMORY_TYPE_ACPI_NVS;
case EfiMemoryMappedIO:
return MEMORY_TYPE_MMIO;
case EfiMemoryMappedIOPortSpace:
return MEMORY_TYPE_MMIO_PORT_SPACE;
case EfiPalCode:
return MEMORY_TYPE_PAL_CODE;
case EfiPersistentMemory:
return MEMORY_TYPE_PERSISTENT;
case EfiReservedMemoryType:
default:
if ((ULONG)EfiMemoryType < MAXLONG) {
return MEMORY_TYPE_RESERVED;
} else {
return (MEMORY_TYPE)EfiMemoryType;
}
}
}
ULONG
MmFwpGetOsAttributeType (
IN UINT64 EfiAttributes
)
/*++
Routine Description:
Translates EFI memory descriptor attributes to NT memory descriptor attributes.
Arguments:
EfiAttributes - the EFI attributes.
Return Value:
The NT attributes.
--*/
{
ULONG NtAttributes;
NtAttributes = 0;
if (EfiAttributes & EFI_MEMORY_UC) {
NtAttributes |= MEMORY_ATTRIBUTE_UC;
}
if (EfiAttributes & EFI_MEMORY_WC) {
NtAttributes |= MEMORY_ATTRIBUTE_WC;
}
if (EfiAttributes & EFI_MEMORY_WT) {
NtAttributes |= MEMORY_ATTRIBUTE_WT;
}
if (EfiAttributes & EFI_MEMORY_WB) {
NtAttributes |= MEMORY_ATTRIBUTE_WB;
}
if (EfiAttributes & EFI_MEMORY_UCE) {
NtAttributes |= MEMORY_ATTRIBUTE_UCE;
}
if (EfiAttributes & EFI_MEMORY_WP) {
NtAttributes |= MEMORY_ATTRIBUTE_WP;
}
if (EfiAttributes & EFI_MEMORY_RP) {
NtAttributes |= MEMORY_ATTRIBUTE_RP;
}
if (EfiAttributes & EFI_MEMORY_XP) {
NtAttributes |= MEMORY_ATTRIBUTE_XP;
}
if (EfiAttributes & EFI_MEMORY_RUNTIME) {
NtAttributes |= MEMORY_ATTRIBUTE_RUNTIME;
}
return NtAttributes;
}
NTSTATUS
MmFwGetMemoryMap (
IN OUT PMEMORY_DESCRIPTOR_LIST Mdl,
IN ULONG Flags
)
/*++
Routine Description:
Gets the memory map from EFI and converts it into an MDL.
Arguments:
Mdl - Pointer to the MDL to store new descriptors in.
Flags - Unused.
Return Value:
STATUS_SUCCESS if successful,
STATUS_INVALID_PARAMETER if Mdl is invalid.
--*/
{
NTSTATUS Status;
EFI_PHYSICAL_ADDRESS EfiBuffer;
EFI_MEMORY_DESCRIPTOR *EfiMap;
UINTN EfiMapKey;
UINTN EfiMapSize, EfiDescriptorSize;
UINT32 EfiDescriptorVersion;
UINT64 EfiStartPage, EfiEndPage;
UINTN EfiPageCount;
ULONGLONG NtStartPage;
ULONG NtPageCount;
BOOLEAN IsRamdisk;
UINT64 EfiRamdiskStartPage, EfiRamdiskEndPage;
MEMORY_TYPE MemoryType;
PMEMORY_DESCRIPTOR NtDescriptor;
(VOID)Flags;
EfiBuffer = (EFI_PHYSICAL_ADDRESS)0;
if (Mdl == NULL) {
return STATUS_INVALID_PARAMETER;
}
MmMdFreeList(Mdl);
//
// Get the memory map from firmware.
// This is a "fake" call to actually just get
// the required buffer size and other info.
//
EfiMapSize = 0;
Status = EfiGetMemoryMap(&EfiMapSize, NULL, &EfiMapKey, &EfiDescriptorSize, &EfiDescriptorVersion);
if (Status != STATUS_BUFFER_TOO_SMALL) {
DebugPrint(L"MmFwGetMemoryMap(): EfiGetMemoryMap() failed\r\n");
//
// Make sure status is not successful, just in case
// EfiGetMemoryMap() somehow succeeded.
//
if (NT_SUCCESS(Status)) {
Status = STATUS_UNSUCCESSFUL;
}
goto exit;
}
EfiMapSize += 4 * EfiDescriptorSize;
NtPageCount = (ALIGN_UP(EfiMapSize, PAGE_SIZE) >> PAGE_SHIFT) + 1;
EfiPageCount = EFI_PAGE(NtPageCount);
Status = EfiAllocatePages(AllocateAnyPages, EfiLoaderData, EfiPageCount, &EfiBuffer);
if (!NT_SUCCESS(Status)) {
DebugPrint(L"MmFwGetMemoryMap(): EfiAllocatePages() failed\r\n");
goto exit;
}
EfiMap = (EFI_MEMORY_DESCRIPTOR *)EfiBuffer;
Status = EfiGetMemoryMap(&EfiMapSize, EfiMap, &EfiMapKey, &EfiDescriptorSize, &EfiDescriptorVersion);
if (!NT_SUCCESS(Status)) {
DebugPrint(L"MmFwGetMemoryMap(): EfiGetMemoryMap() failed\r\n");
goto exit;
}
if (EfiDescriptorSize < sizeof(EFI_MEMORY_DESCRIPTOR) || EfiMapSize % EfiDescriptorSize) {
DebugPrint(L"MmFwGetMemoryMap(): Invalid EFI descriptor/map sizes\r\n");
Status = STATUS_UNSUCCESSFUL;
goto exit;
}
if (BlpBootDevice->Type == BOOT_DEVICE_TYPE_BLOCK && BlpBootDevice->Block.Type == BOOT_BLOCK_DEVICE_TYPE_RAMDISK) {
IsRamdisk = TRUE;
EfiRamdiskStartPage = BlpBootDevice->Block.Ramdisk.ImageBase.QuadPart >> EFI_PAGE_SHIFT;
EfiRamdiskEndPage = EfiRamdiskStartPage + ((BlpBootDevice->Block.Ramdisk.ImageBase.QuadPart + BlpBootDevice->Block.Ramdisk.ImageSize) >> EFI_PAGE_SHIFT);
} else {
IsRamdisk = FALSE;
}
for (
;
EfiMapSize > 0;
EfiMapSize -= EfiDescriptorSize,
EfiMap = (EFI_MEMORY_DESCRIPTOR *)((PUCHAR)EfiMap + EfiDescriptorSize)
) {
//
// Skip desciptors with no pages.
//
if (EfiMap->NumberOfPages == 0) {
continue;
}
MemoryType = BlMmTranslateEfiMemoryType(EfiMap->Type);
if (MemoryType == MEMORY_TYPE_FREE) {
EfiStartPage = ALIGN_UP(EfiMap->PhysicalStart, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT;
} else {
EfiStartPage = EfiMap->PhysicalStart >> EFI_PAGE_SHIFT;
}
EfiEndPage = ALIGN_DOWN(EfiStartPage + EfiMap->NumberOfPages, EFI_PAGE(1));
//
// Regions under 1MiB are mapped differently.
//
if (EfiStartPage < (_1MiB >> EFI_PAGE_SHIFT)) {
//
// Reserve region at page 0.
//
if (EfiStartPage == 0) {
NtDescriptor = MmMdInitDescriptor(
NT_PAGE(EfiStartPage),
0,
1,
MmFwpGetOsAttributeType(EfiMap->Attribute),
MEMORY_TYPE_RESERVED
);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, MDL_OPERATION_FLAGS_TRUNCATE);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
//
// Continue if this descriptor was only one page.
//
if (EfiEndPage == 1) {
continue;
}
}
//
// For regions crossing over the 1MiB boundary,
// create two descriptors. One for <1MiB and one
// for over >=1MiB.
//
if (EfiEndPage > (_1MiB >> EFI_PAGE_SHIFT)) {
NtDescriptor = MmMdInitDescriptor(
NT_PAGE(EfiStartPage),
0,
(_1MiB >> PAGE_SHIFT) - NT_PAGE(EfiStartPage),
MmFwpGetOsAttributeType(EfiMap->Attribute),
MemoryType
);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (NtDescriptor->Type == MEMORY_TYPE_FREE) {
NtDescriptor->Attributes |= MEMORY_ATTRIBUTE_BELOW_1MIB;
}
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, MDL_OPERATION_FLAGS_TRUNCATE);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
//
// Continue to creating the >=1MiB mapping.
//
EfiStartPage = _1MiB >> EFI_PAGE_SHIFT;
}
}
if (IsRamdisk) {
if (
EfiStartPage <= EfiRamdiskStartPage
&& EfiEndPage >= EfiRamdiskEndPage
) {
if (NT_PAGE(EfiStartPage) < NT_PAGE(EfiRamdiskStartPage)) {
NtDescriptor = MmMdInitDescriptor(
NT_PAGE(EfiStartPage),
0,
NT_PAGE(EfiRamdiskStartPage) - NT_PAGE(EfiStartPage),
MmFwpGetOsAttributeType(EfiMap->Attribute),
MemoryType
);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, MDL_OPERATION_FLAGS_TRUNCATE);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
}
//
// Create a memory descriptor for the ramdisk.
//
NtDescriptor = MmMdInitDescriptor(
NT_PAGE(EfiRamdiskStartPage),
0,
NT_PAGE(EfiRamdiskEndPage) - NT_PAGE(EfiRamdiskStartPage),
MmFwpGetOsAttributeType(EfiMap->Attribute),
MemoryType
);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, MDL_OPERATION_FLAGS_TRUNCATE);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
//
// Continue if there is no more memory to map inside the ramdisk.
//
if (NT_PAGE(EfiEndPage) <= NT_PAGE(EfiRamdiskEndPage)) {
continue;
}
EfiStartPage = EFI_PAGE(NT_PAGE(EfiRamdiskEndPage - 1) + 1);
} else if (
NT_PAGE(EfiStartPage) < NT_PAGE(EfiRamdiskStartPage)
&& NT_PAGE(EfiEndPage) > NT_PAGE(EfiRamdiskStartPage)
) {
//
// Remove the region inside the start of the ramdisk.
//
EfiEndPage = EfiRamdiskStartPage;
} else if (
NT_PAGE(EfiStartPage) < NT_PAGE(EfiRamdiskEndPage)
&& NT_PAGE(EfiEndPage) > NT_PAGE(EfiRamdiskEndPage)
) {
//
// Remove the region inside the end of the ramdisk.
//
EfiStartPage = EfiRamdiskEndPage;
}
//
// Continue if the region is now empty.
//
if (NT_PAGE(EfiStartPage) == NT_PAGE(EfiEndPage)) {
continue;
}
}
//
// Create a memory descriptor for the region.
//
NtDescriptor = MmMdInitDescriptor(
NT_PAGE(EfiStartPage),
0,
NT_PAGE(EfiEndPage) - NT_PAGE(EfiStartPage),
MmFwpGetOsAttributeType(EfiMap->Attribute),
MemoryType
);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
//
// Set attribute if below 1MiB.
//
if (NtDescriptor->Type == MEMORY_TYPE_FREE && EfiEndPage <= (_1MiB >> EFI_PAGE_SHIFT)) {
NtDescriptor->Attributes |= MEMORY_ATTRIBUTE_BELOW_1MIB;
}
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, MDL_OPERATION_FLAGS_TRUNCATE);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
}
//
// The following code is to free the buffer and
// also mark the freed memory as free in the MDL.
//
Status = EfiFreePages(EfiBuffer, EfiPageCount);
if (!NT_SUCCESS(Status)) {
Status = STATUS_SUCCESS;
goto exit;
}
EfiBuffer = 0;
EfiStartPage = EfiBuffer >> EFI_PAGE_SHIFT;
EfiEndPage = ALIGN_UP(EfiStartPage + EfiPageCount, EFI_PAGE(1));
NtStartPage = NT_PAGE(EfiStartPage);
NtPageCount = NT_PAGE(EfiEndPage) - NtStartPage;
//
// Find the current descriptor.
//
NtDescriptor = MmMdFindDescriptorFromMdl(Mdl, NtStartPage, MDL_OPERATION_FLAGS_PHYSICAL);
if (NtDescriptor == NULL) {
Status = STATUS_UNSUCCESSFUL;
goto exit;
}
//
// Create a new free descriptor, with the same
// attributes as the current one.
//
NtDescriptor = MmMdInitDescriptor(NtStartPage, 0, NtPageCount, NtDescriptor->Attributes, MEMORY_TYPE_FREE);
if (NtDescriptor == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
//
// Remove the current descriptor.
//
Status = MmMdRemoveRegionFromMdl(Mdl, NtStartPage, NtPageCount, MDL_OPERATION_FLAGS_PHYSICAL);
if (!NT_SUCCESS(Status)) {
MmMdFreeDescriptor(NtDescriptor);
goto exit;
}
//
// Add the new descriptor to the MDL.
//
Status = MmMdAddDescriptorToList(Mdl, NtDescriptor, 0x01);
exit:
if (EfiBuffer) {
EfiFreePages(EfiBuffer, EfiPageCount);
}
if (!NT_SUCCESS(Status)) {
MmMdFreeList(Mdl);
}
return Status;
}