coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
heapmanager.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
5 #include <cbmem.h>
6 #include <string.h>
7 
8 static void *agesa_heap_base(void)
9 {
11 }
12 
13 static void EmptyHeap(int unused)
14 {
15  void *BiosManagerPtr = agesa_heap_base();
16  memset(BiosManagerPtr, 0, BIOS_HEAP_SIZE);
17 }
18 
19 /*
20  * Name agesa_GetTempHeapBase
21  * Brief description Get the location for TempRam, the target location in
22  * memory where AmdInitPost copies the heap prior to CAR
23  * teardown. AmdInitEnv calls this function after
24  * teardown for the source address when relocation the
25  * heap to its final location.
26  * Input parameters
27  * Func Unused
28  * Data Unused
29  * ConfigPtr Pointer to type AGESA_TEMP_HEAP_BASE_PARAMS
30  * Output parameters
31  * Status Indicates whether TempHeapAddress was successfully
32  * set.
33  */
35  void *ConfigPtr)
36 {
37  AGESA_TEMP_HEAP_BASE_PARAMS *pTempHeapBase;
38 
39  pTempHeapBase = (AGESA_TEMP_HEAP_BASE_PARAMS *)ConfigPtr;
40  pTempHeapBase->TempHeapAddress = CONFIG_PI_AGESA_TEMP_RAM_BASE;
41 
42  return AGESA_SUCCESS;
43 }
44 
45 /*
46  * Name agesa_HeapRebase
47  * Brief description AGESA may use internal hardcoded locations for its
48  * heap. Modern implementations allow the base to be
49  * overridden by calling agesa_HeapRebase.
50  * Input parameters
51  * Func Unused
52  * Data Unused
53  * ConfigPtr Pointer to type AGESA_REBASE_PARAMS
54  * Output parameters
55  * Status Indicates whether HeapAddress was successfully
56  * set.
57  */
58 AGESA_STATUS agesa_HeapRebase(uint32_t Func, uintptr_t Data, void *ConfigPtr)
59 {
60  AGESA_REBASE_PARAMS *Rebase;
61 
62  Rebase = (AGESA_REBASE_PARAMS *)ConfigPtr;
63  Rebase->HeapAddress = (uintptr_t)agesa_heap_base();
64  if (!Rebase->HeapAddress)
65  Rebase->HeapAddress = CONFIG_PI_AGESA_CAR_HEAP_BASE;
66 
67  return AGESA_SUCCESS;
68 }
69 
70 /*
71  * Name FindAllocatedNode
72  * Brief description Find an allocated node that matches the handle.
73  * Input parameter The desired handle.
74  * Output parameters
75  * pointer Here is returned either the found node or the last
76  * allocated node if the handle is not found. This is
77  * intentional, as the field NextNode of this node will
78  * have to be filled with the offset of the node being
79  * created in procedure agesa_AllocateBuffer().
80  * Status Indicates if the node was or was not found.
81  */
83  BIOS_BUFFER_NODE **last_allocd_or_match)
84 {
85  uint32_t AllocNodeOffset;
86  uint8_t *BiosHeapBaseAddr;
87  BIOS_BUFFER_NODE *AllocNodePtr;
88  BIOS_HEAP_MANAGER *BiosHeapBasePtr;
89  AGESA_STATUS Status = AGESA_SUCCESS;
90 
91  BiosHeapBaseAddr = agesa_heap_base();
92  BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
93 
94  AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
95  AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
96 
97  while (handle != AllocNodePtr->BufferHandle) {
98  if (AllocNodePtr->NextNodeOffset == 0) {
99  Status = AGESA_BOUNDS_CHK;
100  break;
101  }
102  AllocNodeOffset = AllocNodePtr->NextNodeOffset;
103  AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
104  AllocNodeOffset);
105  }
106  *last_allocd_or_match = AllocNodePtr;
107  return Status;
108 }
109 
110 /*
111  * Name ConcatenateNodes
112  * Brief description Concatenates two adjacent nodes into a single node,
113  * this procedure is used by agesa_DeallocateBuffer().
114  * Input parameters
115  * FirstNodePtr This node is in the front, its header will be
116  * maintained.
117  * SecondNodePtr This node is in the back, its header will be cleared.
118  */
119 static void ConcatenateNodes(BIOS_BUFFER_NODE *FirstNodePtr,
120  BIOS_BUFFER_NODE *SecondNodePtr)
121 {
122  FirstNodePtr->BufferSize += SecondNodePtr->BufferSize +
123  sizeof(BIOS_BUFFER_NODE);
124  FirstNodePtr->NextNodeOffset = SecondNodePtr->NextNodeOffset;
125 
126  /* Zero out the SecondNode header */
127  memset(SecondNodePtr, 0, sizeof(BIOS_BUFFER_NODE));
128 }
129 
131 
133  void *ConfigPtr)
134 {
135  /*
136  * Size variables explanation:
137  * FreedNodeSize - the size of the buffer node being examined,
138  * will be copied to BestFitNodeSize if the node
139  * is selected as a possible best fit.
140  * BestFitNodeSize - the size qf the buffer of the node currently
141  * considered the best fit.
142  * MinimumSize - the requested size + sizeof(BIOS_BUFFER_NODE).
143  * Its the minimum size for the buffer to be broken
144  * down into 2 nodes, once a node is selected as
145  * the best fit.
146  */
147  uint32_t AvailableHeapSize;
148  uint8_t *BiosHeapBaseAddr;
149  uint32_t CurrNodeOffset;
150  uint32_t PrevNodeOffset;
151  uint32_t FreedNodeOffset;
152  uint32_t FreedNodeSize;
153  uint32_t BestFitNodeOffset;
154  uint32_t BestFitNodeSize;
155  uint32_t BestFitPrevNodeOffset;
156  uint32_t NextFreeOffset;
157  uint32_t MinimumSize;
158  BIOS_BUFFER_NODE *CurrNodePtr;
159  BIOS_BUFFER_NODE *FreedNodePtr;
160  BIOS_BUFFER_NODE *BestFitNodePtr;
161  BIOS_BUFFER_NODE *BestFitPrevNodePtr;
162  BIOS_BUFFER_NODE *NextFreePtr;
163  BIOS_HEAP_MANAGER *BiosHeapBasePtr;
164  AGESA_BUFFER_PARAMS *AllocParams;
165  AGESA_STATUS Status;
166 
167  AllocParams = ((AGESA_BUFFER_PARAMS *)ConfigPtr);
168  AllocParams->BufferPointer = NULL;
169  MinimumSize = AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE);
170 
171  AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
172  BestFitNodeSize = AvailableHeapSize; /* init with largest possible */
173  BiosHeapBaseAddr = agesa_heap_base();
174  BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
175 
176  if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
177  /* First allocation */
178  CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
179  CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
180  + CurrNodeOffset);
181  CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
182  CurrNodePtr->BufferSize = AllocParams->BufferLength;
183  CurrNodePtr->NextNodeOffset = 0;
184  AllocParams->BufferPointer = (uint8_t *)CurrNodePtr
185  + sizeof(BIOS_BUFFER_NODE);
186 
187  /* Update the remaining free space */
188  FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize
189  + sizeof(BIOS_BUFFER_NODE);
190  FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
191  + FreedNodeOffset);
192  FreedNodePtr->BufferSize = AvailableHeapSize
193  - (FreedNodeOffset - CurrNodeOffset)
194  - sizeof(BIOS_BUFFER_NODE);
195  FreedNodePtr->NextNodeOffset = 0;
196 
197  /* Update the offsets for Allocated and Freed nodes */
198  BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
199  BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
200  } else {
201  /*
202  * Find out whether BufferHandle has been allocated on the heap.
203  * If it has, return AGESA_BOUNDS_CHK.
204  */
205  Status = FindAllocatedNode(AllocParams->BufferHandle,
206  &CurrNodePtr);
207  if (Status == AGESA_SUCCESS)
208  return AGESA_BOUNDS_CHK;
209 
210  /*
211  * If status ditn't returned AGESA_SUCCESS, CurrNodePtr here
212  * points to the end of the allocated nodes list.
213  */
214 
215  /* Find the node that best fits the requested buffer size */
216  FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
217  PrevNodeOffset = FreedNodeOffset;
218  BestFitNodeOffset = 0;
219  BestFitPrevNodeOffset = 0;
220  while (FreedNodeOffset != 0) {
221  FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
222  + FreedNodeOffset);
223  FreedNodeSize = FreedNodePtr->BufferSize;
224  if (FreedNodeSize >= MinimumSize) {
225  if (BestFitNodeOffset == 0) {
226  /*
227  * First node that fits the requested
228  * buffer size
229  */
230  BestFitNodeOffset = FreedNodeOffset;
231  BestFitPrevNodeOffset = PrevNodeOffset;
232  BestFitNodeSize = FreedNodeSize;
233  } else {
234  /*
235  * Find out whether current node is a
236  * betterfit than the previous nodes
237  */
238  if (BestFitNodeSize > FreedNodeSize) {
239 
240  BestFitNodeOffset =
241  FreedNodeOffset;
242  BestFitPrevNodeOffset =
243  PrevNodeOffset;
244  BestFitNodeSize = FreedNodeSize;
245  }
246  }
247  }
248  PrevNodeOffset = FreedNodeOffset;
249  FreedNodeOffset = FreedNodePtr->NextNodeOffset;
250  } /* end of while loop */
251 
252  if (BestFitNodeOffset == 0) {
253  /*
254  * If we could not find a node that fits the requested
255  * buffer size, return AGESA_BOUNDS_CHK.
256  */
257  return AGESA_BOUNDS_CHK;
258  }
259 
260  BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
261  + BestFitNodeOffset);
262  BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
263  BestFitPrevNodeOffset);
264 
265  /*
266  * If BestFitNode is larger than the requested buffer,
267  * fragment the node further
268  */
269  if (BestFitNodePtr->BufferSize > MinimumSize) {
270  NextFreeOffset = BestFitNodeOffset + MinimumSize;
271  NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
272  NextFreeOffset);
273  NextFreePtr->BufferSize = BestFitNodeSize - MinimumSize;
274 
275  /* Remove BestFitNode from list of Freed nodes */
276  NextFreePtr->NextNodeOffset =
277  BestFitNodePtr->NextNodeOffset;
278  } else {
279  /*
280  * Otherwise, next free node is NextNodeOffset of
281  * BestFitNode. Remove it from list of Freed nodes.
282  */
283  NextFreeOffset = BestFitNodePtr->NextNodeOffset;
284  }
285 
286  /*
287  * If BestFitNode is the first buffer in the list, then
288  * update StartOfFreedNodes to reflect new free node.
289  */
290  if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes)
291  BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
292  else
293  BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
294 
295  /* Add BestFitNode to the list of Allocated nodes */
296  CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
297  BestFitNodePtr->BufferSize = AllocParams->BufferLength;
298  BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
299  BestFitNodePtr->NextNodeOffset = 0;
300 
301  AllocParams->BufferPointer = (uint8_t *)BestFitNodePtr +
302  sizeof(BIOS_BUFFER_NODE);
303  }
304 
305  return AGESA_SUCCESS;
306 }
307 
309  void *ConfigPtr)
310 {
311 
312  uint8_t *BiosHeapBaseAddr;
313  uint32_t AllocNodeOffset;
314  uint32_t PrevNodeOffset;
315  uint32_t NextNodeOffset;
316  uint32_t FreedNodeOffset;
317  uint32_t EndNodeOffset;
318  BIOS_BUFFER_NODE *AllocNodePtr;
319  BIOS_BUFFER_NODE *PrevNodePtr;
320  BIOS_BUFFER_NODE *FreedNodePtr;
321  BIOS_BUFFER_NODE *NextNodePtr;
322  BIOS_HEAP_MANAGER *BiosHeapBasePtr;
323  AGESA_BUFFER_PARAMS *AllocParams;
324 
325  AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
326 
327  BiosHeapBaseAddr = agesa_heap_base();
328  BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
329 
330  /* Find target node to deallocate in list of allocated nodes.
331  * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
332  */
333  AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
334  AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
335  PrevNodeOffset = AllocNodeOffset;
336 
337  while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
338  if (AllocNodePtr->NextNodeOffset == 0)
339  return AGESA_BOUNDS_CHK;
340  PrevNodeOffset = AllocNodeOffset;
341  AllocNodeOffset = AllocNodePtr->NextNodeOffset;
342  AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
343  + AllocNodeOffset);
344  }
345 
346  /* Remove target node from list of allocated nodes */
347  PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
348  PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
349 
350  /* Zero out the buffer, and clear the BufferHandle */
351  memset((uint8_t *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0,
352  AllocNodePtr->BufferSize);
353  AllocNodePtr->BufferHandle = 0;
354 
355  /* Add deallocated node in order to the list of freed nodes */
356  FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
357  FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
358 
359  EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
360  sizeof(BIOS_BUFFER_NODE);
361 
362  if (AllocNodeOffset < FreedNodeOffset) {
363  /* Add to the start of the freed list */
364  if (EndNodeOffset == FreedNodeOffset) {
365  /* If the freed node is adjacent to the first node in
366  * the list, concatenate both nodes
367  */
368  ConcatenateNodes(AllocNodePtr, FreedNodePtr);
369  } else {
370  /* Otherwise, add freed node to the start of the list
371  * Update NextNodeOffset and BufferSize to include the
372  * size of BIOS_BUFFER_NODE.
373  */
374  AllocNodePtr->NextNodeOffset = FreedNodeOffset;
375  }
376  /* Update StartOfFreedNodes to the new first node */
377  BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
378  } else {
379  /* Traverse list of freed nodes to find where the deallocated
380  * node should be placed.
381  */
382  NextNodeOffset = FreedNodeOffset;
383  NextNodePtr = FreedNodePtr;
384  while (AllocNodeOffset > NextNodeOffset) {
385  PrevNodeOffset = NextNodeOffset;
386  if (NextNodePtr->NextNodeOffset == 0)
387  break;
388  NextNodeOffset = NextNodePtr->NextNodeOffset;
389  NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
390  + NextNodeOffset);
391  }
392 
393  /* If deallocated node is adjacent to the next node,
394  * concatenate both nodes.
395  */
396  if (NextNodeOffset == EndNodeOffset) {
397  NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
398  + NextNodeOffset);
399  ConcatenateNodes(AllocNodePtr, NextNodePtr);
400  } else {
401  /*AllocNodePtr->NextNodeOffset =
402  * FreedNodePtr->NextNodeOffset; */
403  AllocNodePtr->NextNodeOffset = NextNodeOffset;
404  }
405  /*
406  * If deallocated node is adjacent to the previous node,
407  * concatenate both nodes.
408  */
409  PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
410  + PrevNodeOffset);
411  EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
412  sizeof(BIOS_BUFFER_NODE);
413 
414  if (AllocNodeOffset == EndNodeOffset)
415  ConcatenateNodes(PrevNodePtr, AllocNodePtr);
416  else
417  PrevNodePtr->NextNodeOffset = AllocNodeOffset;
418  }
419  return AGESA_SUCCESS;
420 }
421 
422 AGESA_STATUS agesa_LocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
423 {
424  BIOS_BUFFER_NODE *AllocNodePtr;
425  AGESA_BUFFER_PARAMS *AllocParams;
426  AGESA_STATUS Status;
427 
428  AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
429 
430  Status = FindAllocatedNode(AllocParams->BufferHandle, &AllocNodePtr);
431 
432  if (Status == AGESA_SUCCESS) {
433  AllocParams->BufferPointer = (uint8_t *)((uint8_t *)AllocNodePtr
434  + sizeof(BIOS_BUFFER_NODE));
435  AllocParams->BufferLength = AllocNodePtr->BufferSize;
436  }
437 
438  return Status;
439 
440 }
#define AGESA_SUCCESS
Definition: Amd.h:38
unsigned int AGESA_STATUS
Definition: Amd.h:36
void * memset(void *dstpp, int c, size_t len)
Definition: memset.c:12
void * cbmem_add(u32 id, u64 size)
Definition: imd_cbmem.c:144
#define ROMSTAGE_CBMEM_INIT_HOOK(init_fn_)
Definition: cbmem.h:134
#define CBMEM_ID_RESUME_SCRATCH
Definition: cbmem_id.h:50
struct _BIOS_BUFFER_NODE BIOS_BUFFER_NODE
void EmptyHeap(void)
Definition: heapmanager.c:36
#define BIOS_HEAP_SIZE
Definition: heapmanager.c:15
struct _BIOS_HEAP_MANAGER BIOS_HEAP_MANAGER
static void * agesa_heap_base(void)
Definition: heapmanager.c:8
AGESA_STATUS agesa_DeallocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
Definition: heapmanager.c:308
AGESA_STATUS agesa_HeapRebase(uint32_t Func, uintptr_t Data, void *ConfigPtr)
Definition: heapmanager.c:58
AGESA_STATUS agesa_GetTempHeapBase(uint32_t Func, uintptr_t Data, void *ConfigPtr)
Definition: heapmanager.c:34
static void ConcatenateNodes(BIOS_BUFFER_NODE *FirstNodePtr, BIOS_BUFFER_NODE *SecondNodePtr)
Definition: heapmanager.c:119
AGESA_STATUS agesa_LocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
Definition: heapmanager.c:422
static AGESA_STATUS FindAllocatedNode(uint32_t handle, BIOS_BUFFER_NODE **last_allocd_or_match)
Definition: heapmanager.c:82
AGESA_STATUS agesa_AllocateBuffer(uint32_t Func, uintptr_t Data, void *ConfigPtr)
Definition: heapmanager.c:132
#define NULL
Definition: stddef.h:19
unsigned int uint32_t
Definition: stdint.h:14
unsigned long uintptr_t
Definition: stdint.h:21
unsigned char uint8_t
Definition: stdint.h:8
UINT32 NextNodeOffset
Definition: heapmanager.c:78
UINT32 StartOfAllocatedNodes
Definition: heapmanager.c:71
UINT32 StartOfFreedNodes
Definition: heapmanager.c:72