blob: a02a6a2e37550f29ffc26d2dd06bd83546522b24 [file] [log] [blame]
/* $NoKeywords:$ */
/**
* @file
*
* AMD Heap Manager and Heap Allocation APIs, and related functions.
*
* Contains code that initialize, maintain, and allocate the heap space.
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: AGESA
* @e sub-project: CPU
* @e \$Revision: 56322 $ @e \$Date: 2011-07-11 16:51:42 -0600 (Mon, 11 Jul 2011) $
*
*/
/*******************************************************************************
*
* Copyright (C) 2012 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Advanced Micro Devices, Inc. nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/*----------------------------------------------------------------------------------------
* M O D U L E S U S E D
*----------------------------------------------------------------------------------------
*/
#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "cpuRegisters.h"
#include "cpuServices.h"
#include "GeneralServices.h"
#include "heapManager.h"
#include "cpuCacheInit.h"
#include "cpuFamilyTranslation.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G2_PEI)
#define FILECODE PROC_CPU_HEAPMANAGER_FILECODE
/*----------------------------------------------------------------------------------------
* D E F I N I T I O N S A N D M A C R O S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* T Y P E D E F S A N D S T R U C T U R E S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* P R O T O T Y P E S O F L O C A L F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
UINT64
STATIC
HeapGetCurrentBase (
IN AMD_CONFIG_PARAMS *StdHeader
);
VOID
STATIC
DeleteFreeSpaceNode (
IN AMD_CONFIG_PARAMS *StdHeader,
IN UINT32 OffsetOfDeletedNode
);
VOID
STATIC
InsertFreeSpaceNode (
IN AMD_CONFIG_PARAMS *StdHeader,
IN UINT32 OffsetOfInsertNode
);
/*----------------------------------------------------------------------------------------
* P U B L I C F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* E X P O R T E D F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
extern BUILD_OPT_CFG UserOptions;
/*---------------------------------------------------------------------------------------*/
/**
* This function initializes the heap for each CPU core.
*
* Check for already initialized. If not, determine offset of local heap in CAS and
* setup initial heap markers and bookkeeping status. Initialize a couple heap items
* all cores need, for convenience. Currently these are caching the AP mailbox info and
* an initial event log.
*
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
* @retval AGESA_SUCCESS This core's heap is initialized
* @retval AGESA_FATAL This core's heap cannot be initialized due to any reasons below:
* - current processor family cannot be identified.
*
*/
AGESA_STATUS
HeapManagerInit (
IN AMD_CONFIG_PARAMS *StdHeader
)
{
// First Time Initialization
// Note: First 16 bytes of buffer is reserved for Heap Manager use
UINT16 HeapAlreadyInitSizeDword;
UINT32 HeapAlreadyRead;
UINT8 L2LineSize;
UINT8 *HeapBufferPtr;
UINT8 *HeapInitPtr;
UINT32 *HeapDataPtr;
UINT64 MsrData;
UINT64 MsrMask;
UINT8 Ignored;
CPUID_DATA CpuId;
BUFFER_NODE *FreeSpaceNode;
CACHE_INFO *CacheInfoPtr;
AGESA_STATUS IgnoredSts;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
CPU_LOGICAL_ID CpuFamilyRevision;
// Check whether this is a known processor family.
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
if ((CpuFamilyRevision.Family == 0) && (CpuFamilyRevision.Revision == 0)) {
IDS_ERROR_TRAP;
return AGESA_FATAL;
}
GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);
FamilySpecificServices->GetCacheInfo (FamilySpecificServices, (CONST VOID **) &CacheInfoPtr, &Ignored, StdHeader);
HeapBufferPtr = (UINT8 *) StdHeader->HeapBasePtr;
// Check whether the heap manager is already initialized
LibAmdMsrRead (AMD_MTRR_VARIABLE_HEAP_MASK, &MsrData, StdHeader);
if (MsrData == (CacheInfoPtr->VariableMtrrMask & AMD_HEAP_MTRR_MASK)) {
LibAmdMsrRead (AMD_MTRR_VARIABLE_HEAP_BASE, &MsrData, StdHeader);
if ((MsrData & CacheInfoPtr->HeapBaseMask) == ((UINT64) HeapBufferPtr & CacheInfoPtr->HeapBaseMask)) {
if (((HEAP_MANAGER *) HeapBufferPtr)->Signature == HEAP_SIGNATURE_VALID) {
// This is not a bug, there are multiple premem basic entry points,
// and each will call heap init to make sure create struct will succeed.
// If that is later deemed a problem, there needs to be a reasonable test
// for the calling code to make to determine if it needs to init heap or not.
// In the mean time, add this to the event log
PutEventLog (AGESA_SUCCESS,
CPU_ERROR_HEAP_IS_ALREADY_INITIALIZED,
0, 0, 0, 0, StdHeader);
return AGESA_SUCCESS;
}
}
}
// Set variable MTRR base and mask
MsrData = ((UINT64) HeapBufferPtr & CacheInfoPtr->HeapBaseMask);
MsrMask = CacheInfoPtr->VariableMtrrHeapMask & AMD_HEAP_MTRR_MASK;
MsrData |= 0x06;
LibAmdMsrWrite (AMD_MTRR_VARIABLE_HEAP_BASE, &MsrData, StdHeader);
LibAmdMsrWrite (AMD_MTRR_VARIABLE_HEAP_MASK, &MsrMask, StdHeader);
// Set top of memory to a temp value
MsrData = (UINT64) (AMD_TEMP_TOM);
LibAmdMsrWrite (TOP_MEM, &MsrData, StdHeader);
// Enable variable MTTRs
LibAmdMsrRead (SYS_CFG, &MsrData, StdHeader);
MsrData |= AMD_VAR_MTRR_ENABLE_BIT;
LibAmdMsrWrite (SYS_CFG, &MsrData, StdHeader);
// Initialize Heap Space
// BIOS may store to a line only after it has been allocated by a load
LibAmdCpuidRead (AMD_CPUID_L2L3Cache_L2TLB, &CpuId, StdHeader);
L2LineSize = (UINT8) (CpuId.ECX_Reg);
HeapInitPtr = HeapBufferPtr ;
for (HeapAlreadyRead = 0; HeapAlreadyRead < AMD_HEAP_SIZE_PER_CORE;
(HeapAlreadyRead = HeapAlreadyRead + L2LineSize)) {
Ignored = *HeapInitPtr;
HeapInitPtr += L2LineSize;
}
HeapDataPtr = (UINT32 *) HeapBufferPtr;
for (HeapAlreadyInitSizeDword = 0; HeapAlreadyInitSizeDword < AMD_HEAP_SIZE_DWORD_PER_CORE; HeapAlreadyInitSizeDword++) {
*HeapDataPtr = 0;
HeapDataPtr++;
}
// Note: We are reserving the first 16 bytes for Heap Manager use
// UsedSize indicates the size of heap spaced is used for HEAP_MANAGER, BUFFER_NODE,
// Pad for 16-byte alignment, buffer data, and IDS SENTINEL.
// FirstActiveBufferOffset is initalized as invalid heap offset, AMD_HEAP_INVALID_HEAP_OFFSET.
// FirstFreeSpaceOffset is initalized as the byte right after HEAP_MANAGER header.
// Then we set Signature of HEAP_MANAGER header as valid, HEAP_SIGNATURE_VALID.
((HEAP_MANAGER*) HeapBufferPtr)->UsedSize = sizeof (HEAP_MANAGER);
((HEAP_MANAGER*) HeapBufferPtr)->FirstActiveBufferOffset = AMD_HEAP_INVALID_HEAP_OFFSET;
((HEAP_MANAGER*) HeapBufferPtr)->FirstFreeSpaceOffset = sizeof (HEAP_MANAGER);
((HEAP_MANAGER*) HeapBufferPtr)->Signature = HEAP_SIGNATURE_VALID;
// Create free space link
FreeSpaceNode = (BUFFER_NODE *) (HeapBufferPtr + sizeof (HEAP_MANAGER));
FreeSpaceNode->BufferSize = AMD_HEAP_SIZE_PER_CORE - sizeof (HEAP_MANAGER) - sizeof (BUFFER_NODE);
FreeSpaceNode->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
StdHeader->HeapStatus = HEAP_LOCAL_CACHE;
if (!IsBsp (StdHeader, &IgnoredSts)) {
// The BSP's hardware mailbox has not been initialized, so only APs
// can do this at this point.
CacheApMailbox (StdHeader);
}
EventLogInitialization (StdHeader);
return AGESA_SUCCESS;
}
/*---------------------------------------------------------------------------------------*/
/**
* Allocates space for a new buffer in the heap
*
* This function will allocate new buffer either by using internal 'AGESA' heapmanager
* or by using externa (IBV) heapmanager. This function will also determine if whether or not
* there is enough space for the new structure. If so, it will zero out the buffer,
* and return a pointer to the region.
*
* @param[in,out] AllocateHeapParams structure pointer containing the size of the
* desired new region, its handle, and the
* return pointer.
* @param[in,out] StdHeader Config handle for library and services.
*
* @retval AGESA_SUCCESS No error
* @retval AGESA_BOUNDS_CHK Handle already exists, or not enough
* free space
* @retval AGESA_UNSUPPORTED Do not support this kind of heap allocation
* @retval AGESA_ERROR Heap is invaild
*
*/
AGESA_STATUS
HeapAllocateBuffer (
IN OUT ALLOCATE_HEAP_PARAMS *AllocateHeapParams,
IN OUT AMD_CONFIG_PARAMS *StdHeader
)
{
UINT8 *BaseAddress;
UINT8 AlignTo16Byte;
UINT8 CalloutFcnData;
UINT32 RemainSize;
UINT32 OffsetOfSplitNode;
UINT32 OffsetOfNode;
HEAP_MANAGER *HeapManager;
BUFFER_NODE *FreeSpaceNode;
BUFFER_NODE *SplitFreeSpaceNode;
BUFFER_NODE *CurrentBufferNode;
BUFFER_NODE *NewBufferNode;
AGESA_BUFFER_PARAMS AgesaBuffer;
ASSERT (StdHeader != NULL);
if (AllocateHeapParams->Persist == HEAP_RUNTIME_SYSTEM_MEM) {
ASSERT (StdHeader->HeapStatus == HEAP_SYSTEM_MEM);
if (StdHeader->HeapStatus != HEAP_SYSTEM_MEM) {
return AGESA_UNSUPPORTED;
}
}
// At this stage we will decide to either use external (IBV) heap manger
// or internal (AGESA) heap manager.
// If (HeapStatus == HEAP_SYSTEM_MEM), then use the call function to call
// external heap manager
if (StdHeader->HeapStatus == HEAP_SYSTEM_MEM) {
AgesaBuffer.StdHeader = *StdHeader;
AgesaBuffer.BufferHandle = AllocateHeapParams->BufferHandle;
AgesaBuffer.BufferLength = AllocateHeapParams->RequestedBufferSize;
if (AllocateHeapParams->Persist == HEAP_RUNTIME_SYSTEM_MEM) {
CalloutFcnData = HEAP_CALLOUT_RUNTIME;
} else {
CalloutFcnData = HEAP_CALLOUT_BOOTTIME;
}
AGESA_TESTPOINT (TpIfBeforeAllocateHeapBuffer, StdHeader);
if (AgesaAllocateBuffer (CalloutFcnData, &AgesaBuffer) != AGESA_SUCCESS) {
AllocateHeapParams->BufferPtr = NULL;
return AGESA_ERROR;
}
AGESA_TESTPOINT (TpIfAfterAllocateHeapBuffer, StdHeader);
AllocateHeapParams->BufferPtr = (UINT8 *) (AgesaBuffer.BufferPointer);
return AGESA_SUCCESS;
}
// If (StdHeader->HeapStatus != HEAP_SYSTEM_MEM), then allocated buffer
// using following AGESA Heap Manager code.
// Buffer pointer is NULL unless we return a buffer.
AlignTo16Byte = 0;
AllocateHeapParams->BufferPtr = NULL;
AllocateHeapParams->RequestedBufferSize += NUM_OF_SENTINEL * SIZE_OF_SENTINEL;
// Get base address
BaseAddress = (UINT8 *) StdHeader->HeapBasePtr;
HeapManager = (HEAP_MANAGER *) BaseAddress;
// Check Heap database is valid
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// The base address in StdHeader is incorrect, get base address by itself
BaseAddress = (UINT8 *) HeapGetBaseAddress (StdHeader);
HeapManager = (HEAP_MANAGER *) BaseAddress;
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// Heap is not available, ASSERT here
ASSERT (FALSE);
return AGESA_ERROR;
}
StdHeader->HeapBasePtr = (UINT64) BaseAddress;
}
// Allocate
CurrentBufferNode = (BUFFER_NODE *) (BaseAddress + sizeof (HEAP_MANAGER));
// If there already has been a heap with the incoming BufferHandle, we return AGESA_BOUNDS_CHK.
if (HeapManager->FirstActiveBufferOffset != AMD_HEAP_INVALID_HEAP_OFFSET) {
CurrentBufferNode = (BUFFER_NODE *) (BaseAddress + HeapManager->FirstActiveBufferOffset);
while (CurrentBufferNode->OffsetOfNextNode != AMD_HEAP_INVALID_HEAP_OFFSET) {
if (CurrentBufferNode->BufferHandle == AllocateHeapParams->BufferHandle) {
PutEventLog (AGESA_BOUNDS_CHK,
CPU_ERROR_HEAP_BUFFER_HANDLE_IS_ALREADY_USED,
AllocateHeapParams->BufferHandle, 0, 0, 0, StdHeader);
return AGESA_BOUNDS_CHK;
} else {
CurrentBufferNode = (BUFFER_NODE *) (BaseAddress + CurrentBufferNode->OffsetOfNextNode);
}
}
if (CurrentBufferNode->BufferHandle == AllocateHeapParams->BufferHandle) {
PutEventLog (AGESA_BOUNDS_CHK,
CPU_ERROR_HEAP_BUFFER_HANDLE_IS_ALREADY_USED,
AllocateHeapParams->BufferHandle, 0, 0, 0, StdHeader);
return AGESA_BOUNDS_CHK;
}
}
// Find the buffer size that first matches the requested buffer size (i.e. the first free buffer of greater size).
OffsetOfNode = HeapManager->FirstFreeSpaceOffset;
FreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfNode);
while (OffsetOfNode != AMD_HEAP_INVALID_HEAP_OFFSET) {
AlignTo16Byte = (UINT8) ((0x10 - (((UINTN) (VOID *) FreeSpaceNode + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL) & 0xF)) & 0xF);
AllocateHeapParams->RequestedBufferSize = (UINT32) (AllocateHeapParams->RequestedBufferSize + AlignTo16Byte);
if (FreeSpaceNode->BufferSize >= AllocateHeapParams->RequestedBufferSize) {
break;
}
AllocateHeapParams->RequestedBufferSize = (UINT32) (AllocateHeapParams->RequestedBufferSize - AlignTo16Byte);
OffsetOfNode = FreeSpaceNode->OffsetOfNextNode;
FreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfNode);
}
if (OffsetOfNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
// We don't find any free space buffer that matches the requested buffer size.
PutEventLog (AGESA_BOUNDS_CHK,
CPU_ERROR_HEAP_IS_FULL,
AllocateHeapParams->BufferHandle, 0, 0, 0, StdHeader);
return AGESA_BOUNDS_CHK;
} else {
// We find one matched free space buffer.
DeleteFreeSpaceNode (StdHeader, OffsetOfNode);
NewBufferNode = FreeSpaceNode;
// Add new buffer node to the buffer chain
if (HeapManager->FirstActiveBufferOffset == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapManager->FirstActiveBufferOffset = sizeof (HEAP_MANAGER);
} else {
CurrentBufferNode->OffsetOfNextNode = OffsetOfNode;
}
// New buffer size
RemainSize = FreeSpaceNode->BufferSize - AllocateHeapParams->RequestedBufferSize;
if (RemainSize > sizeof (BUFFER_NODE)) {
NewBufferNode->BufferSize = AllocateHeapParams->RequestedBufferSize;
OffsetOfSplitNode = OffsetOfNode + sizeof (BUFFER_NODE) + NewBufferNode->BufferSize;
SplitFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfSplitNode);
SplitFreeSpaceNode->BufferSize = RemainSize - sizeof (BUFFER_NODE);
InsertFreeSpaceNode (StdHeader, OffsetOfSplitNode);
} else {
// Remain size is less than BUFFER_NODE, we use whole size instead of requested size.
NewBufferNode->BufferSize = FreeSpaceNode->BufferSize;
}
}
// Initialize BUFFER_NODE structure of NewBufferNode
NewBufferNode->BufferHandle = AllocateHeapParams->BufferHandle;
if ((AllocateHeapParams->Persist == HEAP_TEMP_MEM) || (AllocateHeapParams->Persist == HEAP_SYSTEM_MEM)) {
NewBufferNode->Persist = AllocateHeapParams->Persist;
} else {
NewBufferNode->Persist = HEAP_LOCAL_CACHE;
}
NewBufferNode->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
NewBufferNode->PadSize = AlignTo16Byte;
// Clear to 0x00
LibAmdMemFill ((VOID *) ((UINT8 *) NewBufferNode + sizeof (BUFFER_NODE)), 0x00, NewBufferNode->BufferSize, StdHeader);
// Debug feature
SET_SENTINEL_BEFORE (NewBufferNode, AlignTo16Byte);
SET_SENTINEL_AFTER (NewBufferNode);
// Update global variables
HeapManager->UsedSize += NewBufferNode->BufferSize + sizeof (BUFFER_NODE);
// Now fill in the incoming structure
AllocateHeapParams->BufferPtr = (UINT8 *) ((UINT8 *) NewBufferNode + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL + AlignTo16Byte);
AllocateHeapParams->RequestedBufferSize -= (NUM_OF_SENTINEL * SIZE_OF_SENTINEL + AlignTo16Byte);
return AGESA_SUCCESS;
}
/*---------------------------------------------------------------------------------------*/
/**
* Deallocates a previously allocated buffer in the heap
*
* This function will deallocate buffer either by using internal 'AGESA' heapmanager
* or by using externa (IBV) heapmanager.
*
* @param[in] BufferHandle Handle of the buffer to free.
* @param[in] StdHeader Config handle for library and services.
*
* @retval AGESA_SUCCESS No error
* @retval AGESA_BOUNDS_CHK Handle does not exist on the heap
*
*/
AGESA_STATUS
HeapDeallocateBuffer (
IN UINT32 BufferHandle,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT8 *BaseAddress;
UINT32 NodeSize;
UINT32 OffsetOfFreeSpaceNode;
UINT32 OffsetOfPreviousNode;
UINT32 OffsetOfCurrentNode;
BOOLEAN HeapLocateFlag;
HEAP_MANAGER *HeapManager;
BUFFER_NODE *CurrentNode;
BUFFER_NODE *PreviousNode;
BUFFER_NODE *FreeSpaceNode;
AGESA_BUFFER_PARAMS AgesaBuffer;
ASSERT (StdHeader != NULL);
HeapLocateFlag = TRUE;
BaseAddress = (UINT8 *) StdHeader->HeapBasePtr;
HeapManager = (HEAP_MANAGER *) BaseAddress;
// Check Heap database is valid
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// The base address in StdHeader is incorrect, get base address by itself
BaseAddress = (UINT8 *) HeapGetBaseAddress (StdHeader);
HeapManager = (HEAP_MANAGER *) BaseAddress;
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// Heap is not available, ASSERT here
ASSERT (FALSE);
return AGESA_ERROR;
}
StdHeader->HeapBasePtr = (UINT64) BaseAddress;
}
OffsetOfPreviousNode = AMD_HEAP_INVALID_HEAP_OFFSET;
OffsetOfCurrentNode = HeapManager->FirstActiveBufferOffset;
CurrentNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
// Locate heap
if ((BaseAddress != NULL) && (HeapManager->Signature == HEAP_SIGNATURE_VALID)) {
if (OffsetOfCurrentNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapLocateFlag = FALSE;
} else {
while (CurrentNode->BufferHandle != BufferHandle) {
if (CurrentNode->OffsetOfNextNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapLocateFlag = FALSE;
break;
} else {
OffsetOfPreviousNode = OffsetOfCurrentNode;
OffsetOfCurrentNode = CurrentNode->OffsetOfNextNode;
CurrentNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
}
}
}
} else {
HeapLocateFlag = FALSE;
}
if (HeapLocateFlag == TRUE) {
// CurrentNode points to the buffer which wanted to be deallocated.
// Remove deallocated heap from active buffer chain.
if (OffsetOfPreviousNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapManager->FirstActiveBufferOffset = CurrentNode->OffsetOfNextNode;
} else {
PreviousNode = (BUFFER_NODE *) (BaseAddress + OffsetOfPreviousNode);
PreviousNode->OffsetOfNextNode = CurrentNode->OffsetOfNextNode;
}
// Now, CurrentNode become a free space node.
HeapManager->UsedSize -= CurrentNode->BufferSize + sizeof (BUFFER_NODE);
// Loop free space chain to see if any free space node is just before/after CurrentNode, then merge them.
OffsetOfFreeSpaceNode = HeapManager->FirstFreeSpaceOffset;
FreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfFreeSpaceNode);
while (OffsetOfFreeSpaceNode != AMD_HEAP_INVALID_HEAP_OFFSET) {
if ((OffsetOfFreeSpaceNode + sizeof (BUFFER_NODE) + FreeSpaceNode->BufferSize) == OffsetOfCurrentNode) {
DeleteFreeSpaceNode (StdHeader, OffsetOfFreeSpaceNode);
NodeSize = FreeSpaceNode->BufferSize + CurrentNode->BufferSize + sizeof (BUFFER_NODE);
OffsetOfCurrentNode = OffsetOfFreeSpaceNode;
CurrentNode = FreeSpaceNode;
CurrentNode->BufferSize = NodeSize;
} else if (OffsetOfFreeSpaceNode == (OffsetOfCurrentNode + sizeof (BUFFER_NODE) + CurrentNode->BufferSize)) {
DeleteFreeSpaceNode (StdHeader, OffsetOfFreeSpaceNode);
NodeSize = FreeSpaceNode->BufferSize + CurrentNode->BufferSize + sizeof (BUFFER_NODE);
CurrentNode->BufferSize = NodeSize;
}
OffsetOfFreeSpaceNode = FreeSpaceNode->OffsetOfNextNode;
FreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfFreeSpaceNode);
}
InsertFreeSpaceNode (StdHeader, OffsetOfCurrentNode);
return AGESA_SUCCESS;
} else {
// If HeapStatus == HEAP_SYSTEM_MEM, try callout function
if (StdHeader->HeapStatus == HEAP_SYSTEM_MEM) {
AgesaBuffer.StdHeader = *StdHeader;
AgesaBuffer.BufferHandle = BufferHandle;
AGESA_TESTPOINT (TpIfBeforeDeallocateHeapBuffer, StdHeader);
if (AgesaDeallocateBuffer (0, &AgesaBuffer) != AGESA_SUCCESS) {
return AGESA_ERROR;
}
AGESA_TESTPOINT (TpIfAfterDeallocateHeapBuffer, StdHeader);
return AGESA_SUCCESS;
}
// If we are still unable to locate the buffer handle, return AGESA_BOUNDS_CHK
if ((BaseAddress != NULL) && (HeapManager->Signature == HEAP_SIGNATURE_VALID)) {
PutEventLog (AGESA_BOUNDS_CHK,
CPU_ERROR_HEAP_BUFFER_HANDLE_IS_NOT_PRESENT,
BufferHandle, 0, 0, 0, StdHeader);
} else {
ASSERT (FALSE);
}
return AGESA_BOUNDS_CHK;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Locates a previously allocated buffer on the heap.
*
* This function searches the heap for a buffer with the desired handle, and
* returns a pointer to the buffer.
*
* @param[in,out] LocateHeap Structure containing the buffer's handle,
* and the return pointer.
* @param[in] StdHeader Config handle for library and services.
*
* @retval AGESA_SUCCESS No error
* @retval AGESA_BOUNDS_CHK Handle does not exist on the heap
*
*/
AGESA_STATUS
HeapLocateBuffer (
IN OUT LOCATE_HEAP_PTR *LocateHeap,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT8 *BaseAddress;
UINT8 AlignTo16Byte;
UINT32 OffsetOfCurrentNode;
BOOLEAN HeapLocateFlag;
HEAP_MANAGER *HeapManager;
BUFFER_NODE *CurrentNode;
AGESA_BUFFER_PARAMS AgesaBuffer;
ASSERT (StdHeader != NULL);
HeapLocateFlag = TRUE;
BaseAddress = (UINT8 *) StdHeader->HeapBasePtr;
HeapManager = (HEAP_MANAGER *) BaseAddress;
// Check Heap database is valid
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// The base address in StdHeader is incorrect, get base address by itself
BaseAddress = (UINT8 *) HeapGetBaseAddress (StdHeader);
HeapManager = (HEAP_MANAGER *) BaseAddress;
if ((BaseAddress == NULL) || (HeapManager->Signature != HEAP_SIGNATURE_VALID)) {
// Heap is not available, ASSERT here
ASSERT (FALSE);
return AGESA_ERROR;
}
StdHeader->HeapBasePtr = (UINT64) BaseAddress;
}
OffsetOfCurrentNode = HeapManager->FirstActiveBufferOffset;
CurrentNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
// Find buffer using internal heap manager
// Locate the heap using handle = LocateHeap-> BufferHandle
// If HeapStatus != HEAP_SYSTEM_ MEM
if ((BaseAddress != NULL) && (HeapManager->Signature == HEAP_SIGNATURE_VALID)) {
if (OffsetOfCurrentNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapLocateFlag = FALSE;
} else {
while (CurrentNode->BufferHandle != LocateHeap->BufferHandle) {
if (CurrentNode->OffsetOfNextNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapLocateFlag = FALSE;
break;
} else {
OffsetOfCurrentNode = CurrentNode->OffsetOfNextNode;
CurrentNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
}
}
}
} else {
HeapLocateFlag = FALSE;
}
if (HeapLocateFlag) {
AlignTo16Byte = CurrentNode->PadSize;
LocateHeap->BufferPtr = (UINT8 *) ((UINT8 *) CurrentNode + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL + AlignTo16Byte);
LocateHeap->BufferSize = CurrentNode->BufferSize - NUM_OF_SENTINEL * SIZE_OF_SENTINEL - AlignTo16Byte;
return AGESA_SUCCESS;
} else {
// If HeapStatus == HEAP_SYSTEM_MEM, try callout function
if (StdHeader->HeapStatus == HEAP_SYSTEM_MEM) {
AgesaBuffer.StdHeader = *StdHeader;
AgesaBuffer.BufferHandle = LocateHeap->BufferHandle;
AGESA_TESTPOINT (TpIfBeforeLocateHeapBuffer, StdHeader);
if (AgesaLocateBuffer (0, &AgesaBuffer) != AGESA_SUCCESS) {
LocateHeap->BufferPtr = NULL;
return AGESA_ERROR;
}
LocateHeap->BufferSize = AgesaBuffer.BufferLength;
AGESA_TESTPOINT (TpIfAfterLocateHeapBuffer, StdHeader);
LocateHeap->BufferPtr = (UINT8 *) (AgesaBuffer.BufferPointer);
return AGESA_SUCCESS;
}
// If we are still unable to deallocate the buffer handle, return AGESA_BOUNDS_CHK
LocateHeap->BufferPtr = NULL;
LocateHeap->BufferSize = 0;
if ((BaseAddress != NULL) && (HeapManager->Signature == HEAP_SIGNATURE_VALID)) {
PutEventLog (AGESA_BOUNDS_CHK,
CPU_ERROR_HEAP_BUFFER_HANDLE_IS_NOT_PRESENT,
LocateHeap->BufferHandle, 0, 0, 0, StdHeader);
} else {
ASSERT (FALSE);
}
return AGESA_BOUNDS_CHK;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Get the heap base address
*
* This function will try to locate heap from cache, temp memory, main memory.
* The heap signature will be checked for validity on each possible location.
* Firstly, try if heap base is in cache by calling the function HeapGetCurrentBase.
* Secondly, try if heap base is temp memory by UserOptoions.CfgHeapDramAddress.
* Thirdly, try if heap base is in main memory by doing a buffer locate with buffer handle
* AMD_HEAP_IN_MAIN_MEMORY_HANDLE.
* If no valid heap signature is found in each possible location above, a NULL pointer is returned.
*
* @param[in] StdHeader Config handle for library and services.
*
* @return Heap base address of the executing core's heap.
*
*/
UINT64
HeapGetBaseAddress (
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT64 BaseAddress;
HEAP_MANAGER *HeapManager;
AGESA_BUFFER_PARAMS AgesaBuffer;
// Firstly, we try to see if heap is in cache
BaseAddress = HeapGetCurrentBase (StdHeader);
HeapManager = (HEAP_MANAGER *) BaseAddress;
if ((HeapManager->Signature != HEAP_SIGNATURE_VALID) &&
(StdHeader->HeapStatus != HEAP_DO_NOT_EXIST_YET) &&
(StdHeader->HeapStatus != HEAP_LOCAL_CACHE)) {
// Secondly, we try to see if heap is in temp memory
BaseAddress = UserOptions.CfgHeapDramAddress;
HeapManager = (HEAP_MANAGER *) BaseAddress;
if (HeapManager->Signature != HEAP_SIGNATURE_VALID) {
// Thirdly, we try to see if heap in main memory
// by locating with external buffer manager (IBV)
AgesaBuffer.StdHeader = *StdHeader;
AgesaBuffer.BufferHandle = AMD_HEAP_IN_MAIN_MEMORY_HANDLE;
if (AgesaLocateBuffer (0, &AgesaBuffer) == AGESA_SUCCESS) {
BaseAddress = (UINT64) AgesaBuffer.BufferPointer;
HeapManager = (HEAP_MANAGER *) BaseAddress;
if (HeapManager->Signature != HEAP_SIGNATURE_VALID) {
// No valid heap signature ever found, return a NULL pointer
BaseAddress = 0;
}
} else {
// No heap buffer is allocated by external manager (IBV), return a NULL pointer
BaseAddress = 0;
}
}
}
return BaseAddress;
}
/*---------------------------------------------------------------------------------------
* L O C A L F U N C T I O N S
*---------------------------------------------------------------------------------------
*/
/* -----------------------------------------------------------------------------*/
/**
*
* DeleteFreeSpaceNode
*
* Description:
* Delete a free space node from free space chain
*
* Parameters:
* @param[in] StdHeader Config handle for library and services.
* @param[in] OffsetOfDeletedNode Offset of deleted node.
*
* Processing:
*
*/
VOID
STATIC
DeleteFreeSpaceNode (
IN AMD_CONFIG_PARAMS *StdHeader,
IN UINT32 OffsetOfDeletedNode
)
{
UINT8 *BaseAddress;
UINT32 OffsetOfPreviousNode;
UINT32 OffsetOfCurrentNode;
HEAP_MANAGER *HeapManager;
BUFFER_NODE *CurrentFreeSpaceNode;
BUFFER_NODE *PreviousFreeSpaceNode;
BaseAddress = (UINT8 *) StdHeader->HeapBasePtr;
HeapManager = (HEAP_MANAGER *) BaseAddress;
OffsetOfPreviousNode = AMD_HEAP_INVALID_HEAP_OFFSET;
OffsetOfCurrentNode = HeapManager->FirstFreeSpaceOffset;
//
// After AmdInitEnv, there is no free space provided for HeapAllocateBuffer.
// Hence if the FirstFreeSpaceOffset is AMD_HEAP_INVALID_HEAP_OFFSET, then
// no need to do more on delete node.
//
if (OffsetOfCurrentNode != AMD_HEAP_INVALID_HEAP_OFFSET) {
CurrentFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
while ((OffsetOfCurrentNode != AMD_HEAP_INVALID_HEAP_OFFSET) && (OffsetOfCurrentNode != OffsetOfDeletedNode)) {
OffsetOfPreviousNode = OffsetOfCurrentNode;
OffsetOfCurrentNode = CurrentFreeSpaceNode->OffsetOfNextNode;
CurrentFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
}
if (OffsetOfCurrentNode != AMD_HEAP_INVALID_HEAP_OFFSET) {
if (OffsetOfPreviousNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapManager->FirstFreeSpaceOffset = CurrentFreeSpaceNode->OffsetOfNextNode;
} else {
PreviousFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfPreviousNode);
PreviousFreeSpaceNode->OffsetOfNextNode = CurrentFreeSpaceNode->OffsetOfNextNode;
}
}
}
return;
}
/* -----------------------------------------------------------------------------*/
/**
*
* InsertFreeSpaceNode
*
* Description:
* Insert a free space node to free space chain, size order
*
* Parameters:
* @param[in] StdHeader Config handle for library and services.
* @param[in] OffsetOfInsertNode Offset of inserted node.
*
* Processing:
*
*/
VOID
STATIC
InsertFreeSpaceNode (
IN AMD_CONFIG_PARAMS *StdHeader,
IN UINT32 OffsetOfInsertNode
)
{
UINT8 *BaseAddress;
UINT32 OffsetOfPreviousNode;
UINT32 OffsetOfCurrentNode;
HEAP_MANAGER *HeapManager;
BUFFER_NODE *CurrentFreeSpaceNode;
BUFFER_NODE *PreviousFreeSpaceNode;
BUFFER_NODE *InsertedFreeSpaceNode;
BaseAddress = (UINT8 *) StdHeader->HeapBasePtr;
HeapManager = (HEAP_MANAGER *) BaseAddress;
OffsetOfPreviousNode = AMD_HEAP_INVALID_HEAP_OFFSET;
OffsetOfCurrentNode = HeapManager->FirstFreeSpaceOffset;
CurrentFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
InsertedFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfInsertNode);
while ((OffsetOfCurrentNode != AMD_HEAP_INVALID_HEAP_OFFSET) &&
(CurrentFreeSpaceNode->BufferSize < InsertedFreeSpaceNode->BufferSize)) {
OffsetOfPreviousNode = OffsetOfCurrentNode;
OffsetOfCurrentNode = CurrentFreeSpaceNode->OffsetOfNextNode;
CurrentFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfCurrentNode);
}
InsertedFreeSpaceNode->OffsetOfNextNode = OffsetOfCurrentNode;
if (OffsetOfPreviousNode == AMD_HEAP_INVALID_HEAP_OFFSET) {
HeapManager->FirstFreeSpaceOffset = OffsetOfInsertNode;
} else {
PreviousFreeSpaceNode = (BUFFER_NODE *) (BaseAddress + OffsetOfPreviousNode);
PreviousFreeSpaceNode->OffsetOfNextNode = OffsetOfInsertNode;
}
return;
}
/*---------------------------------------------------------------------------------------*/
/**
* Determines the base address of the executing core's heap.
*
* This function uses the executing core's socket/core numbers to determine
* where it's heap should be located.
*
* @param[in] StdHeader Config handle for library and services.
*
* @return A pointer to the executing core's heap.
*
*/
UINT64
STATIC
HeapGetCurrentBase (
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 SystemCoreNumber;
UINT64 ReturnPtr;
AGESA_STATUS IgnoredStatus;
CPU_SPECIFIC_SERVICES *FamilyServices;
if (IsBsp (StdHeader, &IgnoredStatus)) {
ReturnPtr = AMD_HEAP_START_ADDRESS;
} else {
GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilyServices, StdHeader);
ASSERT (FamilyServices != NULL);
SystemCoreNumber = FamilyServices->GetApCoreNumber (FamilyServices, StdHeader);
ASSERT (SystemCoreNumber != 0);
ASSERT (SystemCoreNumber < 64);
ReturnPtr = ((SystemCoreNumber * AMD_HEAP_SIZE_PER_CORE) + AMD_HEAP_START_ADDRESS);
}
ASSERT (ReturnPtr <= ((AMD_HEAP_REGION_END_ADDRESS + 1) - AMD_HEAP_SIZE_PER_CORE));
return ReturnPtr;
}