mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-10 14:23:31 +01:00
626 lines
20 KiB
C
626 lines
20 KiB
C
|
/** @file
|
||
|
Copyright (C) 2019, vit9696. All rights reserved.
|
||
|
|
||
|
All rights reserved.
|
||
|
|
||
|
This program and the accompanying materials
|
||
|
are licensed and made available under the terms and conditions of the BSD License
|
||
|
which accompanies this distribution. The full text of the license may be found at
|
||
|
http://opensource.org/licenses/bsd-license.php
|
||
|
|
||
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||
|
**/
|
||
|
|
||
|
#include <Uefi.h>
|
||
|
|
||
|
#include <Guid/MemoryAttributesTable.h>
|
||
|
#include <Library/BaseMemoryLib.h>
|
||
|
#include <Library/DebugLib.h>
|
||
|
#include <Library/MemoryAllocationLib.h>
|
||
|
#include <Library/OcGuardLib.h>
|
||
|
#include <Library/OcMemoryLib.h>
|
||
|
#include <Library/UefiBootServicesTableLib.h>
|
||
|
#include <Library/UefiLib.h>
|
||
|
|
||
|
/**
|
||
|
Determine actual memory type from the attribute.
|
||
|
|
||
|
@param[in] MemoryAttribute Attribute to inspect.
|
||
|
**/
|
||
|
STATIC
|
||
|
UINT32
|
||
|
OcRealMemoryType (
|
||
|
IN EFI_MEMORY_DESCRIPTOR *MemoryAttribte
|
||
|
)
|
||
|
{
|
||
|
ASSERT (MemoryAttribte->Type == EfiRuntimeServicesCode
|
||
|
|| MemoryAttribte->Type == EfiRuntimeServicesData);
|
||
|
|
||
|
//
|
||
|
// Use code for write-protected areas.
|
||
|
//
|
||
|
if ((MemoryAttribte->Attribute & EFI_MEMORY_RO) != 0) {
|
||
|
return EfiRuntimeServicesCode;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Use data for executable-protected areas.
|
||
|
//
|
||
|
if ((MemoryAttribte->Attribute & EFI_MEMORY_XP) != 0) {
|
||
|
return EfiRuntimeServicesData;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Use whatever is set.
|
||
|
//
|
||
|
return MemoryAttribte->Type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Split memory map descriptor by attribute.
|
||
|
|
||
|
@param[in,out] RetMemoryMapEntry Pointer to descriptor in the memory map, updated to next proccessed.
|
||
|
@param[in,out] CurrentEntryIndex Current index of the descriptor in the memory map, updated on increase.
|
||
|
@param[in,out] CurrentEntryCount Number of descriptors in the memory map, updated on increase.
|
||
|
@param[in] TotalEntryCount Max number of descriptors in the memory map.
|
||
|
@param[in] MemoryAttribute Memory attribute used for splitting.
|
||
|
@param[in] DescriptorSize Memory map descriptor size.
|
||
|
|
||
|
@retval EFI_SUCCESS on success.
|
||
|
@retval EFI_OUT_OF_RESOURCES when there are not enough free descriptor slots.
|
||
|
**/
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
OcSplitMemoryEntryByAttribute (
|
||
|
IN OUT EFI_MEMORY_DESCRIPTOR **RetMemoryMapEntry,
|
||
|
IN OUT UINTN *CurrentEntryIndex,
|
||
|
IN OUT UINTN *CurrentEntryCount,
|
||
|
IN UINTN TotalEntryCount,
|
||
|
IN EFI_MEMORY_DESCRIPTOR *MemoryAttribute,
|
||
|
IN UINTN DescriptorSize
|
||
|
|
||
|
)
|
||
|
{
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
||
|
EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
|
||
|
UINTN DiffPages;
|
||
|
|
||
|
MemoryMapEntry = *RetMemoryMapEntry;
|
||
|
|
||
|
//
|
||
|
// Memory attribute starts after our descriptor.
|
||
|
// Shorten the existing descriptor and insert the new one after it.
|
||
|
// [DESC1] -> [DESC1][DESC2]
|
||
|
//
|
||
|
if (MemoryAttribute->PhysicalStart > MemoryMapEntry->PhysicalStart) {
|
||
|
if (*CurrentEntryCount == TotalEntryCount) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
||
|
DiffPages = (UINTN) EFI_SIZE_TO_PAGES (MemoryAttribute->PhysicalStart - MemoryMapEntry->PhysicalStart);
|
||
|
CopyMem (
|
||
|
NewMemoryMapEntry,
|
||
|
MemoryMapEntry,
|
||
|
DescriptorSize * (*CurrentEntryCount - *CurrentEntryIndex)
|
||
|
);
|
||
|
MemoryMapEntry->NumberOfPages = DiffPages;
|
||
|
NewMemoryMapEntry->PhysicalStart = MemoryAttribute->PhysicalStart;
|
||
|
NewMemoryMapEntry->NumberOfPages -= DiffPages;
|
||
|
|
||
|
MemoryMapEntry = NewMemoryMapEntry;
|
||
|
|
||
|
//
|
||
|
// Current processed entry is now the one we inserted.
|
||
|
//
|
||
|
++(*CurrentEntryIndex);
|
||
|
++(*CurrentEntryCount);
|
||
|
}
|
||
|
|
||
|
ASSERT (MemoryAttribute->PhysicalStart == MemoryMapEntry->PhysicalStart);
|
||
|
|
||
|
//
|
||
|
// Memory attribute matches our descriptor.
|
||
|
// Simply update its protection.
|
||
|
// [DESC1] -> [DESC1*]
|
||
|
//
|
||
|
if (MemoryMapEntry->NumberOfPages == MemoryAttribute->NumberOfPages) {
|
||
|
MemoryMapEntry->Type = OcRealMemoryType (MemoryAttribute);
|
||
|
*RetMemoryMapEntry = MemoryMapEntry;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Memory attribute is shorter than our descriptor.
|
||
|
// Shorten current descriptor, update its type, and inseret the new one after it.
|
||
|
// [DESC1] -> [DESC1*][DESC2]
|
||
|
//
|
||
|
if (*CurrentEntryCount == TotalEntryCount) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
||
|
CopyMem (
|
||
|
NewMemoryMapEntry,
|
||
|
MemoryMapEntry,
|
||
|
DescriptorSize * (*CurrentEntryCount - *CurrentEntryIndex)
|
||
|
);
|
||
|
MemoryMapEntry->Type = OcRealMemoryType (MemoryAttribute);
|
||
|
MemoryMapEntry->NumberOfPages = MemoryAttribute->NumberOfPages;
|
||
|
NewMemoryMapEntry->PhysicalStart += EFI_PAGES_TO_SIZE (MemoryAttribute->NumberOfPages);
|
||
|
NewMemoryMapEntry->NumberOfPages -= MemoryAttribute->NumberOfPages;
|
||
|
|
||
|
//
|
||
|
// Current processed entry is now the one we need to process.
|
||
|
//
|
||
|
++(*CurrentEntryIndex);
|
||
|
++(*CurrentEntryCount);
|
||
|
|
||
|
*RetMemoryMapEntry = NewMemoryMapEntry;
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Expand attributes table by adding memory map runtime entries into it.
|
||
|
Requires sorted memory map.
|
||
|
|
||
|
@param[in,out] MemoryAttributesTable Memory attributes table.
|
||
|
@param[in,out] MemoryAttributesEntry Memory attributes descriptor.
|
||
|
@param[in] MaxDescriptors Maximum amount of descriptors in the attributes table.
|
||
|
@param[in] MemoryMapDescriptors Memory map descriptor count.
|
||
|
@param[in] MemoryMap Memory map.
|
||
|
@param[in] DescriptorSize Memory map descriptor size.
|
||
|
|
||
|
@retval EFI_SUCCESS on success.
|
||
|
@retval EFI_NOT_FOUND nothing to do.
|
||
|
**/
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
OcExpandAttributesByMap (
|
||
|
IN OUT EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable,
|
||
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry,
|
||
|
IN UINTN MaxDescriptors,
|
||
|
IN UINTN MemoryMapDescriptors,
|
||
|
IN EFI_MEMORY_DESCRIPTOR *MemoryMap,
|
||
|
IN UINTN DescriptorSize
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINTN MapIndex;
|
||
|
UINTN MatIndex;
|
||
|
EFI_PHYSICAL_ADDRESS LastMapAddress;
|
||
|
EFI_PHYSICAL_ADDRESS LastMatAddress;
|
||
|
EFI_PHYSICAL_ADDRESS NewNextGluedAddress;
|
||
|
EFI_PHYSICAL_ADDRESS NextGluedAddress;
|
||
|
BOOLEAN LastMat;
|
||
|
|
||
|
MatIndex = 0;
|
||
|
Status = EFI_NOT_FOUND;
|
||
|
|
||
|
for (MapIndex = 0; MapIndex < MemoryMapDescriptors; ++MapIndex) {
|
||
|
if (MemoryMap->Type == EfiRuntimeServicesCode
|
||
|
|| MemoryMap->Type == EfiRuntimeServicesData) {
|
||
|
|
||
|
LastMapAddress = LAST_DESCRIPTOR_ADDR (MemoryMap);
|
||
|
NextGluedAddress = MemoryMap->PhysicalStart;
|
||
|
LastMat = FALSE;
|
||
|
|
||
|
while (MatIndex < MemoryAttributesTable->NumberOfEntries && !LastMat) {
|
||
|
//
|
||
|
// Process MAT with RT code or RT data type, which is the last one or resides at MAP entry or after.
|
||
|
//
|
||
|
LastMatAddress = LAST_DESCRIPTOR_ADDR (MemoryAttributesEntry);
|
||
|
LastMat = MatIndex + 1 == MemoryAttributesTable->NumberOfEntries;
|
||
|
if ((MemoryMap->Type == EfiRuntimeServicesCode || MemoryMap->Type == EfiRuntimeServicesData)
|
||
|
&& (LastMatAddress >= MemoryMap->PhysicalStart || LastMat)) {
|
||
|
//
|
||
|
// MAT is a suffix of MAP (MAT ends at MAP end).
|
||
|
//
|
||
|
if (NextGluedAddress == MemoryAttributesEntry->PhysicalStart
|
||
|
&& LastMatAddress == LastMapAddress) {
|
||
|
//
|
||
|
// Completed iterating over this MAP entry.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// MAT is a prefix or an infix of MAP:
|
||
|
// 1. MAT entry at the beginning of MAP entry.
|
||
|
// 2. MAT entry continuing previous MAT entry within MAP entry.
|
||
|
// If this MAT is last, we have a hole in the end.
|
||
|
//
|
||
|
if (NextGluedAddress == MemoryAttributesEntry->PhysicalStart && !LastMat) {
|
||
|
//
|
||
|
// MAT is required to be smaller or equal than MAP by UEFI spec.
|
||
|
//
|
||
|
ASSERT (MemoryAttributesEntry->NumberOfPages <= MemoryMap->NumberOfPages);
|
||
|
NextGluedAddress = LastMatAddress + 1;
|
||
|
} else {
|
||
|
//
|
||
|
// Have a hole between neighbouring MAT entries, 3 variants:
|
||
|
// - MAT is within MAP (but starts later).
|
||
|
// - MAT starts after MAP.
|
||
|
// - No MAT fully covers MAP end (this is the last MAT entry).
|
||
|
// Need to insert a new MAT entry to cover the hole.
|
||
|
//
|
||
|
if (MemoryAttributesTable->NumberOfEntries >= MaxDescriptors) {
|
||
|
return EFI_OUT_OF_RESOURCES;
|
||
|
}
|
||
|
|
||
|
if (!LastMat) {
|
||
|
//
|
||
|
// Append to the middle.
|
||
|
// Choose the next processed address. This is the closest boundary for us:
|
||
|
// - First MAT address, when MAT is within MAP.
|
||
|
// - Last MAP address, when MAT starts after MAP.
|
||
|
//
|
||
|
NewNextGluedAddress = MIN (MemoryAttributesEntry->PhysicalStart, LastMapAddress + 1);
|
||
|
//
|
||
|
// Copy existing attributes to the right.
|
||
|
//
|
||
|
ASSERT (MemoryAttributesEntry->PhysicalStart > NextGluedAddress);
|
||
|
CopyMem (
|
||
|
NEXT_MEMORY_DESCRIPTOR (MemoryAttributesEntry, MemoryAttributesTable->DescriptorSize),
|
||
|
MemoryAttributesEntry,
|
||
|
(MemoryAttributesTable->NumberOfEntries - MatIndex) * MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
} else {
|
||
|
//
|
||
|
// Append to the end.
|
||
|
// Next processed address is the end of MAP.
|
||
|
// There are no MATs left to copy.
|
||
|
//
|
||
|
NewNextGluedAddress = LastMapAddress + 1;
|
||
|
//
|
||
|
// If current MAT is a prefix or an infix of MAP, take it into account.
|
||
|
//
|
||
|
if (MemoryAttributesEntry->PhysicalStart == NextGluedAddress) {
|
||
|
NextGluedAddress = LastMatAddress + 1;
|
||
|
} else {
|
||
|
ASSERT (MemoryAttributesEntry->PhysicalStart < NextGluedAddress);
|
||
|
}
|
||
|
//
|
||
|
// Update current entry the new the last entry we are about to write.
|
||
|
//
|
||
|
MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR (MemoryAttributesEntry, MemoryAttributesTable->DescriptorSize);
|
||
|
++MatIndex;
|
||
|
}
|
||
|
//
|
||
|
// Write the new attribute.
|
||
|
//
|
||
|
MemoryAttributesEntry->Type = OcRealMemoryType (MemoryMap);
|
||
|
MemoryAttributesEntry->PhysicalStart = NextGluedAddress;
|
||
|
MemoryAttributesEntry->VirtualStart = 0;
|
||
|
MemoryAttributesEntry->NumberOfPages = EFI_SIZE_TO_PAGES (NewNextGluedAddress - NextGluedAddress);
|
||
|
MemoryAttributesEntry->Attribute = EFI_MEMORY_RUNTIME;
|
||
|
if (MemoryAttributesEntry->Type == EfiRuntimeServicesCode) {
|
||
|
MemoryAttributesEntry->Attribute |= EFI_MEMORY_RO;
|
||
|
} else {
|
||
|
MemoryAttributesEntry->Attribute |= EFI_MEMORY_XP;
|
||
|
}
|
||
|
//
|
||
|
// Update the next processed address.
|
||
|
// Increase the amount of MAT entries in the table.
|
||
|
// Report success.
|
||
|
//
|
||
|
NextGluedAddress = NewNextGluedAddress;
|
||
|
++MemoryAttributesTable->NumberOfEntries;
|
||
|
Status = EFI_SUCCESS;
|
||
|
//
|
||
|
// Done processing MATs.
|
||
|
//
|
||
|
if (NextGluedAddress == LastMapAddress + 1) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Process next MAT.
|
||
|
// Do not increment if it is the last MAT, as we need to fill holes in the end.
|
||
|
//
|
||
|
if (!LastMat) {
|
||
|
MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
++MatIndex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MemoryMap = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryMap,
|
||
|
DescriptorSize
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
EFI_MEMORY_ATTRIBUTES_TABLE *
|
||
|
OcGetMemoryAttributes (
|
||
|
OUT EFI_MEMORY_DESCRIPTOR **MemoryAttributesEntry OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
|
||
|
UINTN Index;
|
||
|
|
||
|
for (Index = 0; Index < gST->NumberOfTableEntries; ++Index) {
|
||
|
if (CompareGuid (&gST->ConfigurationTable[Index].VendorGuid, &gEfiMemoryAttributesTableGuid)) {
|
||
|
MemoryAttributesTable = gST->ConfigurationTable[Index].VendorTable;
|
||
|
if (MemoryAttributesEntry != NULL) {
|
||
|
*MemoryAttributesEntry = (EFI_MEMORY_DESCRIPTOR *) (MemoryAttributesTable + 1);
|
||
|
}
|
||
|
return MemoryAttributesTable;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
OcRebuildAttributes (
|
||
|
IN EFI_PHYSICAL_ADDRESS Address,
|
||
|
IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry;
|
||
|
UINTN MaxDescriptors;
|
||
|
UINTN MemoryMapSize;
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryMap;
|
||
|
UINTN DescriptorSize;
|
||
|
UINTN MapKey;
|
||
|
UINT32 DescriptorVersion;
|
||
|
|
||
|
MemoryAttributesTable = OcGetMemoryAttributes (&MemoryAttributesEntry);
|
||
|
if (MemoryAttributesTable == NULL) {
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Some boards create entry duplicates and lose all non-PE entries
|
||
|
// after loading runtime drivers after EndOfDxe.
|
||
|
// REF: https://github.com/acidanthera/bugtracker/issues/491#issuecomment-609014334
|
||
|
//
|
||
|
MaxDescriptors = MemoryAttributesTable->NumberOfEntries;
|
||
|
Status = OcDeduplicateDescriptors (
|
||
|
&MemoryAttributesTable->NumberOfEntries,
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
//
|
||
|
// Statically allocate memory for the memory map to avoid allocations.
|
||
|
//
|
||
|
STATIC UINT8 mMemoryMap[OC_DEFAULT_MEMORY_MAP_SIZE];
|
||
|
|
||
|
//
|
||
|
// Assume effected and add missing entries.
|
||
|
//
|
||
|
if (GetMemoryMap == NULL) {
|
||
|
GetMemoryMap = gBS->GetMemoryMap;
|
||
|
}
|
||
|
|
||
|
MemoryMapSize = sizeof (mMemoryMap);
|
||
|
MemoryMap = (EFI_MEMORY_DESCRIPTOR *) mMemoryMap;
|
||
|
|
||
|
Status = GetMemoryMap (
|
||
|
&MemoryMapSize,
|
||
|
MemoryMap,
|
||
|
&MapKey,
|
||
|
&DescriptorSize,
|
||
|
&DescriptorVersion
|
||
|
);
|
||
|
|
||
|
if (!EFI_ERROR (Status)) {
|
||
|
OcSortMemoryMap (
|
||
|
MemoryMapSize,
|
||
|
MemoryMap,
|
||
|
DescriptorSize
|
||
|
);
|
||
|
|
||
|
OcExpandAttributesByMap (
|
||
|
MemoryAttributesTable,
|
||
|
MemoryAttributesEntry,
|
||
|
MaxDescriptors,
|
||
|
MemoryMapSize / DescriptorSize,
|
||
|
MemoryMap,
|
||
|
DescriptorSize
|
||
|
);
|
||
|
} else {
|
||
|
//
|
||
|
// TODO: Scream in fear here? We cannot log, but for a fatal error it is "fine".
|
||
|
//
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Some firmwares do not update MAT after loading runtime drivers after EndOfDxe.
|
||
|
// Since the memory used to allocate runtime driver resides in BINs, MAT has whatever
|
||
|
// permissions designated for unused memory. Mark unused memory containing our driver
|
||
|
// as executable here.
|
||
|
// REF: https://github.com/acidanthera/bugtracker/issues/491#issuecomment-606835337
|
||
|
//
|
||
|
if (Address != 0) {
|
||
|
Status = OcUpdateDescriptors (
|
||
|
MemoryAttributesTable->NumberOfEntries * MemoryAttributesTable->DescriptorSize,
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize,
|
||
|
Address,
|
||
|
EfiRuntimeServicesCode,
|
||
|
EFI_MEMORY_RO,
|
||
|
EFI_MEMORY_XP
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
UINTN
|
||
|
OcCountSplitDescritptors (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
UINTN Index;
|
||
|
UINTN DescriptorCount;
|
||
|
CONST EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry;
|
||
|
|
||
|
MemoryAttributesTable = OcGetMemoryAttributes (&MemoryAttributesEntry);
|
||
|
if (MemoryAttributesTable == NULL) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DescriptorCount = 0;
|
||
|
for (Index = 0; Index < MemoryAttributesTable->NumberOfEntries; ++Index) {
|
||
|
if (MemoryAttributesEntry->Type == EfiRuntimeServicesCode
|
||
|
|| MemoryAttributesEntry->Type == EfiRuntimeServicesData) {
|
||
|
++DescriptorCount;
|
||
|
}
|
||
|
|
||
|
MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return DescriptorCount;
|
||
|
}
|
||
|
|
||
|
EFI_STATUS
|
||
|
OcSplitMemoryMapByAttributes (
|
||
|
IN UINTN MaxMemoryMapSize,
|
||
|
IN OUT UINTN *MemoryMapSize,
|
||
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
|
||
|
IN UINTN DescriptorSize
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
CONST EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry;
|
||
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
||
|
EFI_MEMORY_DESCRIPTOR *LastAttributeEntry;
|
||
|
UINTN LastAttributeIndex;
|
||
|
UINTN Index;
|
||
|
UINTN Index2;
|
||
|
UINTN CurrentEntryCount;
|
||
|
UINTN TotalEntryCount;
|
||
|
UINTN AttributeCount;
|
||
|
BOOLEAN CanSplit;
|
||
|
BOOLEAN InDescAttrs;
|
||
|
|
||
|
ASSERT (MaxMemoryMapSize >= *MemoryMapSize);
|
||
|
|
||
|
MemoryAttributesTable = OcGetMemoryAttributes (&MemoryAttributesEntry);
|
||
|
if (MemoryAttributesTable == NULL) {
|
||
|
return EFI_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
LastAttributeEntry = MemoryAttributesEntry;
|
||
|
LastAttributeIndex = 0;
|
||
|
MemoryMapEntry = MemoryMap;
|
||
|
CurrentEntryCount = *MemoryMapSize / DescriptorSize;
|
||
|
TotalEntryCount = MaxMemoryMapSize / DescriptorSize;
|
||
|
AttributeCount = MemoryAttributesTable->NumberOfEntries;
|
||
|
|
||
|
//
|
||
|
// We assume that the memory map and attribute table are sorted.
|
||
|
//
|
||
|
Index = 0;
|
||
|
while (Index < CurrentEntryCount) {
|
||
|
//
|
||
|
// Split entry by as many attributes as possible.
|
||
|
//
|
||
|
CanSplit = TRUE;
|
||
|
while ((MemoryMapEntry->Type == EfiRuntimeServicesCode
|
||
|
|| MemoryMapEntry->Type == EfiRuntimeServicesData) && CanSplit) {
|
||
|
//
|
||
|
// Find corresponding memory attribute.
|
||
|
//
|
||
|
InDescAttrs = FALSE;
|
||
|
MemoryAttributesEntry = LastAttributeEntry;
|
||
|
for (Index2 = LastAttributeIndex; Index2 < AttributeCount; ++Index2) {
|
||
|
if (MemoryAttributesEntry->Type == EfiRuntimeServicesCode
|
||
|
|| MemoryAttributesEntry->Type == EfiRuntimeServicesData) {
|
||
|
//
|
||
|
// UEFI spec says attribute entries are fully within memory map entries.
|
||
|
// Find first one of a different type.
|
||
|
//
|
||
|
if (AREA_WITHIN_DESCRIPTOR (
|
||
|
MemoryMapEntry,
|
||
|
MemoryAttributesEntry->PhysicalStart,
|
||
|
EFI_PAGES_TO_SIZE (MemoryAttributesEntry->NumberOfPages))) {
|
||
|
//
|
||
|
// We are within descriptor attribute sequence.
|
||
|
//
|
||
|
InDescAttrs = TRUE;
|
||
|
//
|
||
|
// No need to process the attribute of the same type.
|
||
|
//
|
||
|
if (OcRealMemoryType (MemoryAttributesEntry) != MemoryMapEntry->Type) {
|
||
|
//
|
||
|
// Start with the next attribute on the second iteration.
|
||
|
//
|
||
|
LastAttributeEntry = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
LastAttributeIndex = Index2 + 1;
|
||
|
break;
|
||
|
}
|
||
|
} else if (InDescAttrs) {
|
||
|
//
|
||
|
// Reached the end of descriptor attribute sequence, abort.
|
||
|
//
|
||
|
InDescAttrs = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryAttributesEntry,
|
||
|
MemoryAttributesTable->DescriptorSize
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (Index2 < AttributeCount && InDescAttrs) {
|
||
|
//
|
||
|
// Split current memory map entry.
|
||
|
//
|
||
|
Status = OcSplitMemoryEntryByAttribute (
|
||
|
&MemoryMapEntry,
|
||
|
&Index,
|
||
|
&CurrentEntryCount,
|
||
|
TotalEntryCount,
|
||
|
MemoryAttributesEntry,
|
||
|
DescriptorSize
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
*MemoryMapSize = CurrentEntryCount * DescriptorSize;
|
||
|
return Status;
|
||
|
}
|
||
|
continue;
|
||
|
} else {
|
||
|
//
|
||
|
// Did not find a suitable attribute or processed all the attributes.
|
||
|
//
|
||
|
CanSplit = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (
|
||
|
MemoryMapEntry,
|
||
|
DescriptorSize
|
||
|
);
|
||
|
++Index;
|
||
|
}
|
||
|
|
||
|
*MemoryMapSize = CurrentEntryCount * DescriptorSize;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|