blob: a503ff51f591efc24e3df5fe13179f6ccc9a951f [file] [log] [blame]
/** @file
SMM Memory pool management functions.
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PiSmmCore.h"
LIST_ENTRY mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX];
//
// To cache the SMRAM base since when Loading modules At fixed address feature is enabled,
// all module is assigned an offset relative the SMRAM base in build time.
//
GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressSmramBase = 0;
/**
Convert a UEFI memory type to SMM pool type.
@param[in] MemoryType Type of pool to allocate.
@return SMM pool type
**/
SMM_POOL_TYPE
UefiMemoryTypeToSmmPoolType (
IN EFI_MEMORY_TYPE MemoryType
)
{
ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData));
switch (MemoryType) {
case EfiRuntimeServicesCode:
return SmmPoolTypeCode;
case EfiRuntimeServicesData:
return SmmPoolTypeData;
default:
return SmmPoolTypeMax;
}
}
/**
Called to initialize the memory service.
@param SmramRangeCount Number of SMRAM Regions
@param SmramRanges Pointer to SMRAM Descriptors
**/
VOID
SmmInitializeMemoryServices (
IN UINTN SmramRangeCount,
IN EFI_SMRAM_DESCRIPTOR *SmramRanges
)
{
UINTN Index;
EFI_STATUS Status;
UINTN SmmPoolTypeIndex;
EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable;
//
// Initialize Pool list
//
for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) {
for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) {
InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]);
}
}
Status = EfiGetSystemConfigurationTable (
&gLoadFixedAddressConfigurationTableGuid,
(VOID **) &LMFAConfigurationTable
);
if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) {
gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase;
}
//
// Add Free SMRAM regions
// Need add Free memory at first, to let gSmmMemoryMap record data
//
for (Index = 0; Index < SmramRangeCount; Index++) {
if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
continue;
}
SmmAddMemoryRegion (
SmramRanges[Index].CpuStart,
SmramRanges[Index].PhysicalSize,
EfiConventionalMemory,
SmramRanges[Index].RegionState
);
}
//
// Add the allocated SMRAM regions
//
for (Index = 0; Index < SmramRangeCount; Index++) {
if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) {
continue;
}
SmmAddMemoryRegion (
SmramRanges[Index].CpuStart,
SmramRanges[Index].PhysicalSize,
EfiConventionalMemory,
SmramRanges[Index].RegionState
);
}
}
/**
Internal Function. Allocate a pool by specified PoolIndex.
@param PoolType Type of pool to allocate.
@param PoolIndex Index which indicate the Pool size.
@param FreePoolHdr The returned Free pool.
@retval EFI_OUT_OF_RESOURCES Allocation failed.
@retval EFI_SUCCESS Pool successfully allocated.
**/
EFI_STATUS
InternalAllocPoolByIndex (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN PoolIndex,
OUT FREE_POOL_HEADER **FreePoolHdr
)
{
EFI_STATUS Status;
FREE_POOL_HEADER *Hdr;
POOL_TAIL *Tail;
EFI_PHYSICAL_ADDRESS Address;
SMM_POOL_TYPE SmmPoolType;
Address = 0;
SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType);
ASSERT (PoolIndex <= MAX_POOL_INDEX);
Status = EFI_SUCCESS;
Hdr = NULL;
if (PoolIndex == MAX_POOL_INDEX) {
Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType,
EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1),
&Address, FALSE);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
} else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) {
Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link);
RemoveEntryList (&Hdr->Link);
} else {
Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr);
if (!EFI_ERROR (Status)) {
Hdr->Header.Signature = 0;
Hdr->Header.Size >>= 1;
Hdr->Header.Available = TRUE;
Hdr->Header.Type = 0;
Tail = HEAD_TO_TAIL(&Hdr->Header);
Tail->Signature = 0;
Tail->Size = 0;
InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link);
Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
}
}
if (!EFI_ERROR (Status)) {
Hdr->Header.Signature = POOL_HEAD_SIGNATURE;
Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
Hdr->Header.Available = FALSE;
Hdr->Header.Type = PoolType;
Tail = HEAD_TO_TAIL(&Hdr->Header);
Tail->Signature = POOL_TAIL_SIGNATURE;
Tail->Size = Hdr->Header.Size;
}
*FreePoolHdr = Hdr;
return Status;
}
/**
Internal Function. Free a pool by specified PoolIndex.
@param FreePoolHdr The pool to free.
@param PoolTail The pointer to the pool tail.
@retval EFI_SUCCESS Pool successfully freed.
**/
EFI_STATUS
InternalFreePoolByIndex (
IN FREE_POOL_HEADER *FreePoolHdr,
IN POOL_TAIL *PoolTail
)
{
UINTN PoolIndex;
SMM_POOL_TYPE SmmPoolType;
ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type);
PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
FreePoolHdr->Header.Signature = 0;
FreePoolHdr->Header.Available = TRUE;
FreePoolHdr->Header.Type = 0;
PoolTail->Signature = 0;
PoolTail->Size = 0;
ASSERT (PoolIndex < MAX_POOL_INDEX);
InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link);
return EFI_SUCCESS;
}
/**
Allocate pool of a particular type.
@param PoolType Type of pool to allocate.
@param Size The amount of pool to allocate.
@param Buffer The address to return a pointer to the allocated
pool.
@retval EFI_INVALID_PARAMETER PoolType not valid.
@retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
@retval EFI_SUCCESS Pool successfully allocated.
**/
EFI_STATUS
EFIAPI
SmmInternalAllocatePool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN Size,
OUT VOID **Buffer
)
{
POOL_HEADER *PoolHdr;
POOL_TAIL *PoolTail;
FREE_POOL_HEADER *FreePoolHdr;
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Address;
UINTN PoolIndex;
BOOLEAN HasPoolTail;
BOOLEAN NeedGuard;
UINTN NoPages;
Address = 0;
if (PoolType != EfiRuntimeServicesCode &&
PoolType != EfiRuntimeServicesData) {
return EFI_INVALID_PARAMETER;
}
NeedGuard = IsPoolTypeToGuard (PoolType);
HasPoolTail = !(NeedGuard &&
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
//
// Adjust the size by the pool header & tail overhead
//
Size += POOL_OVERHEAD;
if (Size > MAX_POOL_SIZE || NeedGuard) {
if (!HasPoolTail) {
Size -= sizeof (POOL_TAIL);
}
NoPages = EFI_SIZE_TO_PAGES (Size);
Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, NoPages,
&Address, NeedGuard);
if (EFI_ERROR (Status)) {
return Status;
}
if (NeedGuard) {
ASSERT (VerifyMemoryGuard (Address, NoPages) == TRUE);
Address = (EFI_PHYSICAL_ADDRESS)(UINTN)AdjustPoolHeadA (
Address,
NoPages,
Size
);
}
PoolHdr = (POOL_HEADER*)(UINTN)Address;
PoolHdr->Signature = POOL_HEAD_SIGNATURE;
PoolHdr->Size = EFI_PAGES_TO_SIZE (NoPages);
PoolHdr->Available = FALSE;
PoolHdr->Type = PoolType;
if (HasPoolTail) {
PoolTail = HEAD_TO_TAIL (PoolHdr);
PoolTail->Signature = POOL_TAIL_SIGNATURE;
PoolTail->Size = PoolHdr->Size;
}
*Buffer = PoolHdr + 1;
return Status;
}
Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
if ((Size & (Size - 1)) != 0) {
PoolIndex++;
}
Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr);
if (!EFI_ERROR(Status)) {
*Buffer = &FreePoolHdr->Header + 1;
}
return Status;
}
/**
Allocate pool of a particular type.
@param PoolType Type of pool to allocate.
@param Size The amount of pool to allocate.
@param Buffer The address to return a pointer to the allocated
pool.
@retval EFI_INVALID_PARAMETER PoolType not valid.
@retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
@retval EFI_SUCCESS Pool successfully allocated.
**/
EFI_STATUS
EFIAPI
SmmAllocatePool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN Size,
OUT VOID **Buffer
)
{
EFI_STATUS Status;
Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
if (!EFI_ERROR (Status)) {
SmmCoreUpdateProfile (
(EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
MemoryProfileActionAllocatePool,
PoolType,
Size,
*Buffer,
NULL
);
}
return Status;
}
/**
Frees pool.
@param Buffer The allocated pool entry to free.
@retval EFI_INVALID_PARAMETER Buffer is not a valid value.
@retval EFI_SUCCESS Pool successfully freed.
**/
EFI_STATUS
EFIAPI
SmmInternalFreePool (
IN VOID *Buffer
)
{
FREE_POOL_HEADER *FreePoolHdr;
POOL_TAIL *PoolTail;
BOOLEAN HasPoolTail;
BOOLEAN MemoryGuarded;
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
MemoryGuarded = IsHeapGuardEnabled () &&
IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer);
HasPoolTail = !(MemoryGuarded &&
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
ASSERT (FreePoolHdr->Header.Signature == POOL_HEAD_SIGNATURE);
ASSERT (!FreePoolHdr->Header.Available);
if (FreePoolHdr->Header.Signature != POOL_HEAD_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
if (HasPoolTail) {
PoolTail = HEAD_TO_TAIL (&FreePoolHdr->Header);
ASSERT (PoolTail->Signature == POOL_TAIL_SIGNATURE);
ASSERT (FreePoolHdr->Header.Size == PoolTail->Size);
if (PoolTail->Signature != POOL_TAIL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
if (FreePoolHdr->Header.Size != PoolTail->Size) {
return EFI_INVALID_PARAMETER;
}
} else {
PoolTail = NULL;
}
if (MemoryGuarded) {
Buffer = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr);
return SmmInternalFreePages (
(EFI_PHYSICAL_ADDRESS)(UINTN)Buffer,
EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size),
TRUE
);
}
if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
return SmmInternalFreePages (
(EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size),
FALSE
);
}
return InternalFreePoolByIndex (FreePoolHdr, PoolTail);
}
/**
Frees pool.
@param Buffer The allocated pool entry to free.
@retval EFI_INVALID_PARAMETER Buffer is not a valid value.
@retval EFI_SUCCESS Pool successfully freed.
**/
EFI_STATUS
EFIAPI
SmmFreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
Status = SmmInternalFreePool (Buffer);
if (!EFI_ERROR (Status)) {
SmmCoreUpdateProfile (
(EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
MemoryProfileActionFreePool,
EfiMaxMemoryType,
0,
Buffer,
NULL
);
}
return Status;
}