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 
3 #include <AGESA.h>
4 #include <amdlib.h>
5 
6 #include <cbmem.h>
9 
10 #include <acpi/acpi.h>
11 #include <console/console.h>
12 #include <string.h>
13 
14 /* BIOS_HEAP_START_ADDRESS is only for cold boots. */
15 #define BIOS_HEAP_SIZE 0x30000
16 #define BIOS_HEAP_START_ADDRESS 0x010000000
17 
18 #if CONFIG(HAVE_ACPI_RESUME) && (HIGH_MEMORY_SCRATCH < BIOS_HEAP_SIZE)
19 #error Increase HIGH_MEMORY_SCRATCH allocation
20 #endif
21 
22 void *GetHeapBase(void)
23 {
24  void *heap = (void *)BIOS_HEAP_START_ADDRESS;
25 
26  if (acpi_is_wakeup_s3()) {
27  /* FIXME: For S3 resume path, buffer is in CBMEM
28  * with some arbitrary header. */
30  heap += 0x10;
31  }
32 
33  return heap;
34 }
35 
36 void EmptyHeap(void)
37 {
38  void *base = GetHeapBase();
40 
41  printk(BIOS_DEBUG, "Wiped HEAP at [%08x - %08x]\n",
42  (unsigned int)(uintptr_t) base, (unsigned int)(uintptr_t) base + BIOS_HEAP_SIZE - 1);
43 }
44 
45 #if defined(HEAP_CALLOUT_RUNTIME) && ENV_RAMSTAGE
46 
47 #define AGESA_RUNTIME_SIZE 4096
48 static AGESA_STATUS alloc_cbmem(AGESA_BUFFER_PARAMS *AllocParams)
49 {
50  static unsigned int used = 0;
52 
53  if ((AGESA_RUNTIME_SIZE - used) < AllocParams->BufferLength) {
54  return AGESA_BOUNDS_CHK;
55  }
56 
57  /* first time allocation */
58  if (!p) {
59  p = cbmem_add(CBMEM_ID_AGESA_RUNTIME, AGESA_RUNTIME_SIZE);
60  if (!p)
61  return AGESA_BOUNDS_CHK;
62  }
63 
64  AllocParams->BufferPointer = p + used;
65  used += AllocParams->BufferLength;
66  return AGESA_SUCCESS;
67 }
68 #endif
69 
70 typedef struct _BIOS_HEAP_MANAGER {
74 
75 typedef struct _BIOS_BUFFER_NODE {
76  UINT32 BufferHandle;
77  UINT32 BufferSize;
80 
82  AGESA_BUFFER_PARAMS *AllocParams)
83 {
84  UINT32 AvailableHeapSize;
85  UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
86  UINT32 CurrNodeOffset;
87  UINT32 PrevNodeOffset;
88  UINT32 FreedNodeOffset;
89  UINT32 BestFitNodeOffset;
90  UINT32 BestFitPrevNodeOffset;
91  UINT32 NextFreeOffset;
92  BIOS_BUFFER_NODE *CurrNodePtr;
93  BIOS_BUFFER_NODE *FreedNodePtr;
94  BIOS_BUFFER_NODE *BestFitNodePtr;
95  BIOS_BUFFER_NODE *BestFitPrevNodePtr;
96  BIOS_BUFFER_NODE *NextFreePtr;
97 
98  AllocParams->BufferPointer = NULL;
99  AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
100 
101  if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
102  /* First allocation */
103  CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
104  CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
105  CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
106  CurrNodePtr->BufferSize = AllocParams->BufferLength;
107  CurrNodePtr->NextNodeOffset = 0;
108  AllocParams->BufferPointer = (UINT8 *) CurrNodePtr + sizeof(BIOS_BUFFER_NODE);
109 
110  /* Update the remaining free space */
111  FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof(BIOS_BUFFER_NODE);
112  FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
113  FreedNodePtr->BufferSize = AvailableHeapSize
114  - (FreedNodeOffset - CurrNodeOffset)
115  - sizeof(BIOS_BUFFER_NODE);
116  FreedNodePtr->NextNodeOffset = 0;
117 
118  /* Update the offsets for Allocated and Freed nodes */
119  BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
120  BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
121  } else {
122  /* Find out whether BufferHandle has been allocated on the heap.
123  * If it has, return AGESA_BOUNDS_CHK.
124  */
125  CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
126  CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
127 
128  while (CurrNodeOffset != 0) {
129  CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
130  if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
131  return AGESA_BOUNDS_CHK;
132  }
133  CurrNodeOffset = CurrNodePtr->NextNodeOffset;
134  /* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
135  * to the end of the allocated nodes list.
136  */
137  }
138  /* Find the node that best fits the requested buffer size */
139  FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
140  PrevNodeOffset = FreedNodeOffset;
141  BestFitNodeOffset = 0;
142  BestFitPrevNodeOffset = 0;
143  while (FreedNodeOffset != 0) {
144  FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
145  if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE))) {
146  if (BestFitNodeOffset == 0) {
147  /* First node that fits the requested buffer size */
148  BestFitNodeOffset = FreedNodeOffset;
149  BestFitPrevNodeOffset = PrevNodeOffset;
150  } else {
151  /* Find out whether current node is a better fit than the previous nodes */
152  BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
153  if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
154  BestFitNodeOffset = FreedNodeOffset;
155  BestFitPrevNodeOffset = PrevNodeOffset;
156  }
157  }
158  }
159  PrevNodeOffset = FreedNodeOffset;
160  FreedNodeOffset = FreedNodePtr->NextNodeOffset;
161  } /* end of while loop */
162 
163  if (BestFitNodeOffset == 0) {
164  /* If we could not find a node that fits the requested buffer
165  * size, return AGESA_BOUNDS_CHK.
166  */
167  return AGESA_BOUNDS_CHK;
168  } else {
169  BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
170  BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);
171 
172  /* If BestFitNode is larger than the requested buffer, fragment the node further */
173  if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE))) {
174  NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE);
175 
176  NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
177  NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE));
178  NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
179  } else {
180  /* Otherwise, next free node is NextNodeOffset of BestFitNode */
181  NextFreeOffset = BestFitNodePtr->NextNodeOffset;
182  }
183 
184  /* If BestFitNode is the first buffer in the list, then update
185  * StartOfFreedNodes to reflect the new free node.
186  */
187  if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
188  BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
189  } else {
190  BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
191  }
192 
193  /* Add BestFitNode to the list of Allocated nodes */
194  CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
195  BestFitNodePtr->BufferSize = AllocParams->BufferLength;
196  BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
197  BestFitNodePtr->NextNodeOffset = 0;
198 
199  /* Remove BestFitNode from list of Freed nodes */
200  AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof(BIOS_BUFFER_NODE);
201  }
202  }
203 
204  return AGESA_SUCCESS;
205 }
206 
208  AGESA_BUFFER_PARAMS *AllocParams)
209 {
210  UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
211  UINT32 AllocNodeOffset;
212  UINT32 PrevNodeOffset;
213  UINT32 NextNodeOffset;
214  UINT32 FreedNodeOffset;
215  UINT32 EndNodeOffset;
216  BIOS_BUFFER_NODE *AllocNodePtr;
217  BIOS_BUFFER_NODE *PrevNodePtr;
218  BIOS_BUFFER_NODE *FreedNodePtr;
219  BIOS_BUFFER_NODE *NextNodePtr;
220 
221  /* Find target node to deallocate in list of allocated nodes.
222  * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
223  */
224  AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
225  AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
226  PrevNodeOffset = AllocNodeOffset;
227 
228  while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
229  if (AllocNodePtr->NextNodeOffset == 0) {
230  return AGESA_BOUNDS_CHK;
231  }
232  PrevNodeOffset = AllocNodeOffset;
233  AllocNodeOffset = AllocNodePtr->NextNodeOffset;
234  AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
235  }
236 
237  /* Remove target node from list of allocated nodes */
238  PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
239  PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
240 
241  /* Zero out the buffer, and clear the BufferHandle */
242  LibAmdMemFill ((UINT8 *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
243  AllocNodePtr->BufferHandle = 0;
244 
245  /* Add deallocated node in order to the list of freed nodes */
246  FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
247  FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
248 
249  EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
250  sizeof(BIOS_BUFFER_NODE);
251 
252  if (AllocNodeOffset < FreedNodeOffset) {
253  /* Add to the start of the freed list */
254  if (EndNodeOffset == FreedNodeOffset) {
255  /* If the freed node is adjacent to the first node in the list, concatenate both nodes */
256  AllocNodePtr->BufferSize += FreedNodePtr->BufferSize +
257  sizeof(BIOS_BUFFER_NODE);
258  AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;
259 
260  /* Zero out the FreedNode header */
261  memset((UINT8 *)FreedNodePtr, 0,
262  sizeof(BIOS_BUFFER_NODE));
263  } else {
264  /* Otherwise, add freed node to the start of the list
265  * Update NextNodeOffset and BufferSize to include the
266  * size of BIOS_BUFFER_NODE.
267  */
268  AllocNodePtr->NextNodeOffset = FreedNodeOffset;
269  }
270  /* Update StartOfFreedNodes to the new first node */
271  BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
272  } else {
273  /* Traverse list of freed nodes to find where the deallocated node
274  * should be placed.
275  */
276  NextNodeOffset = FreedNodeOffset;
277  NextNodePtr = FreedNodePtr;
278  while (AllocNodeOffset > NextNodeOffset) {
279  PrevNodeOffset = NextNodeOffset;
280  if (NextNodePtr->NextNodeOffset == 0) {
281  break;
282  }
283  NextNodeOffset = NextNodePtr->NextNodeOffset;
284  NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
285  }
286 
287  /* If deallocated node is adjacent to the next node,
288  * concatenate both nodes.
289  */
290  if (NextNodeOffset == EndNodeOffset) {
291  NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
292  AllocNodePtr->BufferSize += NextNodePtr->BufferSize +
293  sizeof(BIOS_BUFFER_NODE);
294  AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
295 
296  /* Zero out the NextNode header */
297  memset((UINT8 *)NextNodePtr, 0,
298  sizeof(BIOS_BUFFER_NODE));
299  } else {
300  /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
301  AllocNodePtr->NextNodeOffset = NextNodeOffset;
302  }
303  /* If deallocated node is adjacent to the previous node,
304  * concatenate both nodes.
305  */
306  PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
307  EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
308  sizeof(BIOS_BUFFER_NODE);
309  if (AllocNodeOffset == EndNodeOffset) {
310  PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
311  PrevNodePtr->BufferSize += AllocNodePtr->BufferSize +
312  sizeof(BIOS_BUFFER_NODE);
313 
314  /* Zero out the AllocNode header */
315  memset((UINT8 *)AllocNodePtr, 0,
316  sizeof(BIOS_BUFFER_NODE));
317  } else {
318  PrevNodePtr->NextNodeOffset = AllocNodeOffset;
319  }
320  }
321  return AGESA_SUCCESS;
322 }
323 
325  AGESA_BUFFER_PARAMS *AllocParams)
326 {
327  UINT32 AllocNodeOffset;
328  UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
329  BIOS_BUFFER_NODE *AllocNodePtr;
330 
331  AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
332  AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
333 
334  while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
335  if (AllocNodePtr->NextNodeOffset == 0) {
336  AllocParams->BufferPointer = NULL;
337  AllocParams->BufferLength = 0;
338  return AGESA_BOUNDS_CHK;
339  } else {
340  AllocNodeOffset = AllocNodePtr->NextNodeOffset;
341  AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
342  }
343  }
344 
345  AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof(BIOS_BUFFER_NODE));
346  AllocParams->BufferLength = AllocNodePtr->BufferSize;
347 
348  return AGESA_SUCCESS;
349 
350 }
351 
352 AGESA_STATUS HeapManagerCallout(UINT32 Func, UINTN Data, VOID *ConfigPtr)
353 {
354  AGESA_BUFFER_PARAMS *AllocParams = ConfigPtr;
355 
356 #if defined(HEAP_CALLOUT_RUNTIME) && ENV_RAMSTAGE
357  if (Func == AGESA_ALLOCATE_BUFFER && Data == HEAP_CALLOUT_RUNTIME)
358  return alloc_cbmem(AllocParams);
359 #endif
360 
361  /* Must not call GetHeapBase() in AGESA_UNSUPPORTED path. */
362  if (Func == AGESA_LOCATE_BUFFER)
363  return agesa_LocateBuffer(GetHeapBase(), AllocParams);
364  else if (Func == AGESA_ALLOCATE_BUFFER)
365  return agesa_AllocateBuffer(GetHeapBase(), AllocParams);
366  else if (Func == AGESA_DEALLOCATE_BUFFER)
367  return agesa_DeallocateBuffer(GetHeapBase(), AllocParams);
368 
369  return AGESA_UNSUPPORTED;
370 }
#define AGESA_SUCCESS
Definition: Amd.h:38
unsigned int AGESA_STATUS
Definition: Amd.h:36
#define AGESA_UNSUPPORTED
Definition: Amd.h:41
static int acpi_is_wakeup_s3(void)
Definition: acpi.h:9
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
void * cbmem_find(u32 id)
Definition: imd_cbmem.c:166
#define CBMEM_ID_RESUME_SCRATCH
Definition: cbmem_id.h:50
#define CBMEM_ID_AGESA_RUNTIME
Definition: cbmem_id.h:13
#define printk(level,...)
Definition: stdlib.h:16
#define BIOS_HEAP_START_ADDRESS
Definition: heapmanager.c:16
static AGESA_STATUS agesa_DeallocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr, AGESA_BUFFER_PARAMS *AllocParams)
Definition: heapmanager.c:207
static AGESA_STATUS agesa_AllocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr, AGESA_BUFFER_PARAMS *AllocParams)
Definition: heapmanager.c:81
struct _BIOS_BUFFER_NODE BIOS_BUFFER_NODE
void EmptyHeap(void)
Definition: heapmanager.c:36
void * GetHeapBase(void)
Definition: heapmanager.c:22
#define BIOS_HEAP_SIZE
Definition: heapmanager.c:15
AGESA_STATUS HeapManagerCallout(UINT32 Func, UINTN Data, VOID *ConfigPtr)
Definition: heapmanager.c:352
struct _BIOS_HEAP_MANAGER BIOS_HEAP_MANAGER
static AGESA_STATUS agesa_LocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr, AGESA_BUFFER_PARAMS *AllocParams)
Definition: heapmanager.c:324
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
uintptr_t base
Definition: uart.c:17
#define NULL
Definition: stddef.h:19
unsigned long uintptr_t
Definition: stdint.h:21
UINT32 NextNodeOffset
Definition: heapmanager.c:78
UINT32 StartOfAllocatedNodes
Definition: heapmanager.c:71
UINT32 StartOfFreedNodes
Definition: heapmanager.c:72