blob: 34a4837d73dc72cefc9e4a69364654810e551051 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/agesawrapper.h>
#include <amdblocks/BiosCallOuts.h>
#include <cbmem.h>
#include <string.h>
static void *agesa_heap_base(void)
{
return cbmem_add(CBMEM_ID_RESUME_SCRATCH, BIOS_HEAP_SIZE);
}
static void EmptyHeap(int unused)
{
void *BiosManagerPtr = agesa_heap_base();
memset(BiosManagerPtr, 0, BIOS_HEAP_SIZE);
}
/*
* Name agesa_GetTempHeapBase
* Brief description Get the location for TempRam, the target location in
* memory where AmdInitPost copies the heap prior to CAR
* teardown. AmdInitEnv calls this function after
* teardown for the source address when relocation the
* heap to its final location.
* Input parameters
* Func Unused
* Data Unused
* ConfigPtr Pointer to type AGESA_TEMP_HEAP_BASE_PARAMS
* Output parameters
* Status Indicates whether TempHeapAddress was successfully
* set.
*/
AGESA_STATUS agesa_GetTempHeapBase(uint32_t Func, uintptr_t Data,
void *ConfigPtr)
{
AGESA_TEMP_HEAP_BASE_PARAMS *pTempHeapBase;
pTempHeapBase = (AGESA_TEMP_HEAP_BASE_PARAMS *)ConfigPtr;
pTempHeapBase->TempHeapAddress = CONFIG_PI_AGESA_TEMP_RAM_BASE;
return AGESA_SUCCESS;
}
/*
* Name agesa_HeapRebase
* Brief description AGESA may use internal hardcoded locations for its
* heap. Modern implementations allow the base to be
* overridden by calling agesa_HeapRebase.
* Input parameters
* Func Unused
* Data Unused
* ConfigPtr Pointer to type AGESA_REBASE_PARAMS
* Output parameters
* Status Indicates whether HeapAddress was successfully
* set.
*/
AGESA_STATUS agesa_HeapRebase(uint32_t Func, uintptr_t Data, void *ConfigPtr)
{
AGESA_REBASE_PARAMS *Rebase;
Rebase = (AGESA_REBASE_PARAMS *)ConfigPtr;
Rebase->HeapAddress = (uintptr_t)agesa_heap_base();
if (!Rebase->HeapAddress)
Rebase->HeapAddress = CONFIG_PI_AGESA_CAR_HEAP_BASE;
return AGESA_SUCCESS;
}
/*
* Name FindAllocatedNode
* Brief description Find an allocated node that matches the handle.
* Input parameter The desired handle.
* Output parameters
* pointer Here is returned either the found node or the last
* allocated node if the handle is not found. This is
* intentional, as the field NextNode of this node will
* have to be filled with the offset of the node being
* created in procedure agesa_AllocateBuffer().
* Status Indicates if the node was or was not found.
*/
static AGESA_STATUS FindAllocatedNode(uint32_t handle,
BIOS_BUFFER_NODE **last_allocd_or_match)
{
uint32_t AllocNodeOffset;
uint8_t *BiosHeapBaseAddr;
BIOS_BUFFER_NODE *AllocNodePtr;
BIOS_HEAP_MANAGER *BiosHeapBasePtr;
AGESA_STATUS Status = AGESA_SUCCESS;
BiosHeapBaseAddr = agesa_heap_base();
BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
while (handle != AllocNodePtr->BufferHandle) {
if (AllocNodePtr->NextNodeOffset == 0) {
Status = AGESA_BOUNDS_CHK;
break;
}
AllocNodeOffset = AllocNodePtr->NextNodeOffset;
AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
AllocNodeOffset);
}
*last_allocd_or_match = AllocNodePtr;
return Status;
}
/*
* Name ConcatenateNodes
* Brief description Concatenates two adjacent nodes into a single node,
* this procedure is used by agesa_DeallocateBuffer().
* Input parameters
* FirstNodePtr This node is in the front, its header will be
* maintained.
* SecondNodePtr This node is in the back, its header will be cleared.
*/
static void ConcatenateNodes(BIOS_BUFFER_NODE *FirstNodePtr,
BIOS_BUFFER_NODE *SecondNodePtr)
{
FirstNodePtr->BufferSize += SecondNodePtr->BufferSize +
sizeof(BIOS_BUFFER_NODE);
FirstNodePtr->NextNodeOffset = SecondNodePtr->NextNodeOffset;
/* Zero out the SecondNode header */
memset(SecondNodePtr, 0, sizeof(BIOS_BUFFER_NODE));
}
CBMEM_CREATION_HOOK(EmptyHeap);
AGESA_STATUS agesa_AllocateBuffer(uint32_t Func, uintptr_t Data,
void *ConfigPtr)
{
/*
* Size variables explanation:
* FreedNodeSize - the size of the buffer node being examined,
* will be copied to BestFitNodeSize if the node
* is selected as a possible best fit.
* BestFitNodeSize - the size qf the buffer of the node currently
* considered the best fit.
* MinimumSize - the requested size + sizeof(BIOS_BUFFER_NODE).
* Its the minimum size for the buffer to be broken
* down into 2 nodes, once a node is selected as
* the best fit.
*/
uint32_t AvailableHeapSize;
uint8_t *BiosHeapBaseAddr;
uint32_t CurrNodeOffset;
uint32_t PrevNodeOffset;
uint32_t FreedNodeOffset;
uint32_t FreedNodeSize;
uint32_t BestFitNodeOffset;
uint32_t BestFitNodeSize;
uint32_t BestFitPrevNodeOffset;
uint32_t NextFreeOffset;
uint32_t MinimumSize;
BIOS_BUFFER_NODE *CurrNodePtr;
BIOS_BUFFER_NODE *FreedNodePtr;
BIOS_BUFFER_NODE *BestFitNodePtr;
BIOS_BUFFER_NODE *BestFitPrevNodePtr;
BIOS_BUFFER_NODE *NextFreePtr;
BIOS_HEAP_MANAGER *BiosHeapBasePtr;
AGESA_BUFFER_PARAMS *AllocParams;
AGESA_STATUS Status;
AllocParams = ((AGESA_BUFFER_PARAMS *)ConfigPtr);
AllocParams->BufferPointer = NULL;
MinimumSize = AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE);
AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
BestFitNodeSize = AvailableHeapSize; /* init with largest possible */
BiosHeapBaseAddr = agesa_heap_base();
BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
/* First allocation */
CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ CurrNodeOffset);
CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
CurrNodePtr->BufferSize = AllocParams->BufferLength;
CurrNodePtr->NextNodeOffset = 0;
AllocParams->BufferPointer = (uint8_t *)CurrNodePtr
+ sizeof(BIOS_BUFFER_NODE);
/* Update the remaining free space */
FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize
+ sizeof(BIOS_BUFFER_NODE);
FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ FreedNodeOffset);
FreedNodePtr->BufferSize = AvailableHeapSize
- (FreedNodeOffset - CurrNodeOffset)
- sizeof(BIOS_BUFFER_NODE);
FreedNodePtr->NextNodeOffset = 0;
/* Update the offsets for Allocated and Freed nodes */
BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
} else {
/*
* Find out whether BufferHandle has been allocated on the heap.
* If it has, return AGESA_BOUNDS_CHK.
*/
Status = FindAllocatedNode(AllocParams->BufferHandle,
&CurrNodePtr);
if (Status == AGESA_SUCCESS)
return AGESA_BOUNDS_CHK;
/*
* If status ditn't returned AGESA_SUCCESS, CurrNodePtr here
* points to the end of the allocated nodes list.
*/
/* Find the node that best fits the requested buffer size */
FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
PrevNodeOffset = FreedNodeOffset;
BestFitNodeOffset = 0;
BestFitPrevNodeOffset = 0;
while (FreedNodeOffset != 0) {
FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ FreedNodeOffset);
FreedNodeSize = FreedNodePtr->BufferSize;
if (FreedNodeSize >= MinimumSize) {
if (BestFitNodeOffset == 0) {
/*
* First node that fits the requested
* buffer size
*/
BestFitNodeOffset = FreedNodeOffset;
BestFitPrevNodeOffset = PrevNodeOffset;
BestFitNodeSize = FreedNodeSize;
} else {
/*
* Find out whether current node is a
* betterfit than the previous nodes
*/
if (BestFitNodeSize > FreedNodeSize) {
BestFitNodeOffset =
FreedNodeOffset;
BestFitPrevNodeOffset =
PrevNodeOffset;
BestFitNodeSize = FreedNodeSize;
}
}
}
PrevNodeOffset = FreedNodeOffset;
FreedNodeOffset = FreedNodePtr->NextNodeOffset;
} /* end of while loop */
if (BestFitNodeOffset == 0) {
/*
* If we could not find a node that fits the requested
* buffer size, return AGESA_BOUNDS_CHK.
*/
return AGESA_BOUNDS_CHK;
}
BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ BestFitNodeOffset);
BestFitPrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
BestFitPrevNodeOffset);
/*
* If BestFitNode is larger than the requested buffer,
* fragment the node further
*/
if (BestFitNodePtr->BufferSize > MinimumSize) {
NextFreeOffset = BestFitNodeOffset + MinimumSize;
NextFreePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
NextFreeOffset);
NextFreePtr->BufferSize = BestFitNodeSize - MinimumSize;
/* Remove BestFitNode from list of Freed nodes */
NextFreePtr->NextNodeOffset =
BestFitNodePtr->NextNodeOffset;
} else {
/*
* Otherwise, next free node is NextNodeOffset of
* BestFitNode. Remove it from list of Freed nodes.
*/
NextFreeOffset = BestFitNodePtr->NextNodeOffset;
}
/*
* If BestFitNode is the first buffer in the list, then
* update StartOfFreedNodes to reflect new free node.
*/
if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes)
BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
else
BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
/* Add BestFitNode to the list of Allocated nodes */
CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
BestFitNodePtr->BufferSize = AllocParams->BufferLength;
BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
BestFitNodePtr->NextNodeOffset = 0;
AllocParams->BufferPointer = (uint8_t *)BestFitNodePtr +
sizeof(BIOS_BUFFER_NODE);
}
return AGESA_SUCCESS;
}
AGESA_STATUS agesa_DeallocateBuffer(uint32_t Func, uintptr_t Data,
void *ConfigPtr)
{
uint8_t *BiosHeapBaseAddr;
uint32_t AllocNodeOffset;
uint32_t PrevNodeOffset;
uint32_t NextNodeOffset;
uint32_t FreedNodeOffset;
uint32_t EndNodeOffset;
BIOS_BUFFER_NODE *AllocNodePtr;
BIOS_BUFFER_NODE *PrevNodePtr;
BIOS_BUFFER_NODE *FreedNodePtr;
BIOS_BUFFER_NODE *NextNodePtr;
BIOS_HEAP_MANAGER *BiosHeapBasePtr;
AGESA_BUFFER_PARAMS *AllocParams;
AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
BiosHeapBaseAddr = agesa_heap_base();
BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
/* Find target node to deallocate in list of allocated nodes.
* Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
*/
AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
PrevNodeOffset = AllocNodeOffset;
while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
if (AllocNodePtr->NextNodeOffset == 0)
return AGESA_BOUNDS_CHK;
PrevNodeOffset = AllocNodeOffset;
AllocNodeOffset = AllocNodePtr->NextNodeOffset;
AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ AllocNodeOffset);
}
/* Remove target node from list of allocated nodes */
PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
/* Zero out the buffer, and clear the BufferHandle */
memset((uint8_t *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0,
AllocNodePtr->BufferSize);
AllocNodePtr->BufferHandle = 0;
/* Add deallocated node in order to the list of freed nodes */
FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
sizeof(BIOS_BUFFER_NODE);
if (AllocNodeOffset < FreedNodeOffset) {
/* Add to the start of the freed list */
if (EndNodeOffset == FreedNodeOffset) {
/* If the freed node is adjacent to the first node in
* the list, concatenate both nodes
*/
ConcatenateNodes(AllocNodePtr, FreedNodePtr);
} else {
/* Otherwise, add freed node to the start of the list
* Update NextNodeOffset and BufferSize to include the
* size of BIOS_BUFFER_NODE.
*/
AllocNodePtr->NextNodeOffset = FreedNodeOffset;
}
/* Update StartOfFreedNodes to the new first node */
BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
} else {
/* Traverse list of freed nodes to find where the deallocated
* node should be placed.
*/
NextNodeOffset = FreedNodeOffset;
NextNodePtr = FreedNodePtr;
while (AllocNodeOffset > NextNodeOffset) {
PrevNodeOffset = NextNodeOffset;
if (NextNodePtr->NextNodeOffset == 0)
break;
NextNodeOffset = NextNodePtr->NextNodeOffset;
NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ NextNodeOffset);
}
/* If deallocated node is adjacent to the next node,
* concatenate both nodes.
*/
if (NextNodeOffset == EndNodeOffset) {
NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ NextNodeOffset);
ConcatenateNodes(AllocNodePtr, NextNodePtr);
} else {
/*AllocNodePtr->NextNodeOffset =
* FreedNodePtr->NextNodeOffset; */
AllocNodePtr->NextNodeOffset = NextNodeOffset;
}
/*
* If deallocated node is adjacent to the previous node,
* concatenate both nodes.
*/
PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
+ PrevNodeOffset);
EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
sizeof(BIOS_BUFFER_NODE);
if (AllocNodeOffset == EndNodeOffset)
ConcatenateNodes(PrevNodePtr, AllocNodePtr);
else
PrevNodePtr->NextNodeOffset = AllocNodeOffset;
}
return AGESA_SUCCESS;
}
AGESA_STATUS agesa_LocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
{
BIOS_BUFFER_NODE *AllocNodePtr;
AGESA_BUFFER_PARAMS *AllocParams;
AGESA_STATUS Status;
AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
Status = FindAllocatedNode(AllocParams->BufferHandle, &AllocNodePtr);
if (Status == AGESA_SUCCESS) {
AllocParams->BufferPointer = (uint8_t *)((uint8_t *)AllocNodePtr
+ sizeof(BIOS_BUFFER_NODE));
AllocParams->BufferLength = AllocNodePtr->BufferSize;
}
return Status;
}