soc/amd/common: Make agesa_heap_base non-static
[coreboot.git] / src / soc / amd / common / block / pi / heapmanager.c
blob6b47ec3ac02cd5e523b2d6f8bdd41c8503bd77c6
1 /*
2 * This file is part of the coreboot project.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include <amdblocks/agesawrapper.h>
16 #include <amdlib.h>
17 #include <arch/acpi.h>
18 #include <amdblocks/BiosCallOuts.h>
19 #include <cbmem.h>
20 #include <string.h>
22 void *agesa_heap_base(void)
24 struct cbmem_usage *heap;
25 heap = (struct cbmem_usage *)cbmem_add(CBMEM_ID_RESUME_SCRATCH,
26 sizeof(struct cbmem_usage));
27 return &heap->heap_base;
30 static void EmptyHeap(int unused)
32 void *BiosManagerPtr = agesa_heap_base();
33 memset(BiosManagerPtr, 0, BIOS_HEAP_SIZE);
36 #if IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
37 #error "Only EARLY_CBMEM_INIT is supported."
38 #endif
39 ROMSTAGE_CBMEM_INIT_HOOK(EmptyHeap)
41 AGESA_STATUS agesa_AllocateBuffer (UINT32 Func, UINTN Data, VOID *ConfigPtr)
43 UINT32 AvailableHeapSize;
44 UINT8 *BiosHeapBaseAddr;
45 UINT32 CurrNodeOffset;
46 UINT32 PrevNodeOffset;
47 UINT32 FreedNodeOffset;
48 UINT32 BestFitNodeOffset;
49 UINT32 BestFitPrevNodeOffset;
50 UINT32 NextFreeOffset;
51 BIOS_BUFFER_NODE *CurrNodePtr;
52 BIOS_BUFFER_NODE *FreedNodePtr;
53 BIOS_BUFFER_NODE *BestFitNodePtr;
54 BIOS_BUFFER_NODE *BestFitPrevNodePtr;
55 BIOS_BUFFER_NODE *NextFreePtr;
56 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
57 AGESA_BUFFER_PARAMS *AllocParams;
59 AllocParams = ((AGESA_BUFFER_PARAMS *)ConfigPtr);
60 AllocParams->BufferPointer = NULL;
62 AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
63 BiosHeapBaseAddr = agesa_heap_base();
64 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
66 if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
67 /* First allocation */
68 CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
69 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
70 + CurrNodeOffset);
71 CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
72 CurrNodePtr->BufferSize = AllocParams->BufferLength;
73 CurrNodePtr->NextNodeOffset = 0;
74 AllocParams->BufferPointer = (UINT8 *)CurrNodePtr
75 + sizeof(BIOS_BUFFER_NODE);
77 /* Update the remaining free space */
78 FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize
79 + sizeof(BIOS_BUFFER_NODE);
80 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
81 + FreedNodeOffset);
82 FreedNodePtr->BufferSize = AvailableHeapSize
83 - (FreedNodeOffset - CurrNodeOffset)
84 - sizeof(BIOS_BUFFER_NODE);
85 FreedNodePtr->NextNodeOffset = 0;
87 /* Update the offsets for Allocated and Freed nodes */
88 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
89 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
90 } else {
91 /* Find out whether BufferHandle has been allocated on the heap.
92 * If it has, return AGESA_BOUNDS_CHK.
94 CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
95 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
96 + CurrNodeOffset);
98 while (CurrNodeOffset != 0) {
99 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
100 + CurrNodeOffset);
101 if (CurrNodePtr->BufferHandle ==
102 AllocParams->BufferHandle) {
103 return AGESA_BOUNDS_CHK;
105 CurrNodeOffset = CurrNodePtr->NextNodeOffset;
106 /* If BufferHandle has not been allocated on the heap,
107 * CurrNodePtr here points to the end of the allocated
108 * nodes list.
111 /* Find the node that best fits the requested buffer size */
112 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
113 PrevNodeOffset = FreedNodeOffset;
114 BestFitNodeOffset = 0;
115 BestFitPrevNodeOffset = 0;
116 while (FreedNodeOffset != 0) { /* todo: simplify this */
117 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
118 + FreedNodeOffset);
119 if (FreedNodePtr->BufferSize >=
120 (AllocParams->BufferLength +
121 sizeof(BIOS_BUFFER_NODE))) {
122 if (BestFitNodeOffset == 0) {
124 * First node that fits the requested
125 * buffer size
127 BestFitNodeOffset = FreedNodeOffset;
128 BestFitPrevNodeOffset = PrevNodeOffset;
129 } else {
131 * Find out whether current node is a
132 * betterfit than the previous nodes
134 BestFitNodePtr = (BIOS_BUFFER_NODE *)
135 (BiosHeapBaseAddr +
136 BestFitNodeOffset);
137 if (BestFitNodePtr->BufferSize >
138 FreedNodePtr->BufferSize) {
140 BestFitNodeOffset =
141 FreedNodeOffset;
142 BestFitPrevNodeOffset =
143 PrevNodeOffset;
147 PrevNodeOffset = FreedNodeOffset;
148 FreedNodeOffset = FreedNodePtr->NextNodeOffset;
149 } /* end of while loop */
151 if (BestFitNodeOffset == 0) {
153 * If we could not find a node that fits the requested
154 * buffer size, return AGESA_BOUNDS_CHK.
156 return AGESA_BOUNDS_CHK;
159 BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
160 + BestFitNodeOffset);
161 BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
162 BestFitPrevNodeOffset);
165 * If BestFitNode is larger than the requested buffer,
166 * fragment the node further
168 if (BestFitNodePtr->BufferSize >
169 (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE))) {
170 NextFreeOffset = BestFitNodeOffset +
171 AllocParams->BufferLength +
172 sizeof(BIOS_BUFFER_NODE);
173 NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr +
174 NextFreeOffset);
175 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize -
176 (AllocParams->BufferLength +
177 sizeof(BIOS_BUFFER_NODE));
178 NextFreePtr->NextNodeOffset =
179 BestFitNodePtr->NextNodeOffset;
180 } else {
182 * Otherwise, next free node is
183 * NextNodeOffset of BestFitNode
185 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
189 * If BestFitNode is the first buffer in the list, then
190 * update StartOfFreedNodes to reflect new free node.
192 if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes)
193 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
194 else
195 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
197 /* Add BestFitNode to the list of Allocated nodes */
198 CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
199 BestFitNodePtr->BufferSize = AllocParams->BufferLength;
200 BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
201 BestFitNodePtr->NextNodeOffset = 0;
203 /* Remove BestFitNode from list of Freed nodes */
204 AllocParams->BufferPointer = (UINT8 *)BestFitNodePtr +
205 sizeof(BIOS_BUFFER_NODE);
208 return AGESA_SUCCESS;
211 AGESA_STATUS agesa_DeallocateBuffer (UINT32 Func, UINTN Data, VOID *ConfigPtr)
214 UINT8 *BiosHeapBaseAddr;
215 UINT32 AllocNodeOffset;
216 UINT32 PrevNodeOffset;
217 UINT32 NextNodeOffset;
218 UINT32 FreedNodeOffset;
219 UINT32 EndNodeOffset;
220 BIOS_BUFFER_NODE *AllocNodePtr;
221 BIOS_BUFFER_NODE *PrevNodePtr;
222 BIOS_BUFFER_NODE *FreedNodePtr;
223 BIOS_BUFFER_NODE *NextNodePtr;
224 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
225 AGESA_BUFFER_PARAMS *AllocParams;
227 AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
229 BiosHeapBaseAddr = agesa_heap_base();
230 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
232 /* Find target node to deallocate in list of allocated nodes.
233 * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
235 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
236 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
237 PrevNodeOffset = AllocNodeOffset;
239 while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
240 if (AllocNodePtr->NextNodeOffset == 0)
241 return AGESA_BOUNDS_CHK;
242 PrevNodeOffset = AllocNodeOffset;
243 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
244 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
245 + AllocNodeOffset);
248 /* Remove target node from list of allocated nodes */
249 PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
250 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
252 /* Zero out the buffer, and clear the BufferHandle */
253 memset((UINT8 *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0,
254 AllocNodePtr->BufferSize);
255 AllocNodePtr->BufferHandle = 0;
257 /* Add deallocated node in order to the list of freed nodes */
258 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
259 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
261 EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
262 sizeof(BIOS_BUFFER_NODE);
264 if (AllocNodeOffset < FreedNodeOffset) {
265 /* Add to the start of the freed list */
266 if (EndNodeOffset == FreedNodeOffset) {
267 /* If the freed node is adjacent to the first node in
268 * the list, concatenate both nodes
270 AllocNodePtr->BufferSize += FreedNodePtr->BufferSize +
271 sizeof(BIOS_BUFFER_NODE);
272 AllocNodePtr->NextNodeOffset =
273 FreedNodePtr->NextNodeOffset;
275 /* Zero out the FreedNode header */
276 memset((UINT8 *)FreedNodePtr, 0,
277 sizeof(BIOS_BUFFER_NODE));
278 } else {
279 /* Otherwise, add freed node to the start of the list
280 * Update NextNodeOffset and BufferSize to include the
281 * size of BIOS_BUFFER_NODE.
283 AllocNodePtr->NextNodeOffset = FreedNodeOffset;
285 /* Update StartOfFreedNodes to the new first node */
286 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
287 } else {
288 /* Traverse list of freed nodes to find where the deallocated
289 * node should be placed.
291 NextNodeOffset = FreedNodeOffset;
292 NextNodePtr = FreedNodePtr;
293 while (AllocNodeOffset > NextNodeOffset) {
294 PrevNodeOffset = NextNodeOffset;
295 if (NextNodePtr->NextNodeOffset == 0)
296 break;
297 NextNodeOffset = NextNodePtr->NextNodeOffset;
298 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
299 + NextNodeOffset);
302 /* If deallocated node is adjacent to the next node,
303 * concatenate both nodes.
305 if (NextNodeOffset == EndNodeOffset) {
306 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
307 + NextNodeOffset);
308 AllocNodePtr->BufferSize += NextNodePtr->BufferSize +
309 sizeof(BIOS_BUFFER_NODE);
310 AllocNodePtr->NextNodeOffset =
311 NextNodePtr->NextNodeOffset;
313 /* Zero out the NextNode header */
314 memset((UINT8 *)NextNodePtr, 0,
315 sizeof(BIOS_BUFFER_NODE));
316 } else {
317 /*AllocNodePtr->NextNodeOffset =
318 * FreedNodePtr->NextNodeOffset; */
319 AllocNodePtr->NextNodeOffset = NextNodeOffset;
322 * If deallocated node is adjacent to the previous node,
323 * concatenate both nodes.
325 PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
326 + PrevNodeOffset);
327 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
328 sizeof(BIOS_BUFFER_NODE);
330 if (AllocNodeOffset == EndNodeOffset) {
331 PrevNodePtr->NextNodeOffset =
332 AllocNodePtr->NextNodeOffset;
333 PrevNodePtr->BufferSize += AllocNodePtr->BufferSize +
334 sizeof(BIOS_BUFFER_NODE);
336 /* Zero out the AllocNode header */
337 memset((UINT8 *)AllocNodePtr, 0,
338 sizeof(BIOS_BUFFER_NODE));
339 } else {
340 PrevNodePtr->NextNodeOffset = AllocNodeOffset;
343 return AGESA_SUCCESS;
346 AGESA_STATUS agesa_LocateBuffer (UINT32 Func, UINTN Data, VOID *ConfigPtr)
348 UINT32 AllocNodeOffset;
349 UINT8 *BiosHeapBaseAddr;
350 BIOS_BUFFER_NODE *AllocNodePtr;
351 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
352 AGESA_BUFFER_PARAMS *AllocParams;
354 AllocParams = (AGESA_BUFFER_PARAMS *)ConfigPtr;
356 BiosHeapBaseAddr = agesa_heap_base();
357 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *)BiosHeapBaseAddr;
359 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
360 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
362 while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
363 if (AllocNodePtr->NextNodeOffset == 0) {
364 AllocParams->BufferPointer = NULL;
365 AllocParams->BufferLength = 0;
366 return AGESA_BOUNDS_CHK;
368 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
369 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
370 AllocNodeOffset);
373 AllocParams->BufferPointer = (UINT8 *)((UINT8 *)AllocNodePtr
374 + sizeof(BIOS_BUFFER_NODE));
375 AllocParams->BufferLength = AllocNodePtr->BufferSize;
377 return AGESA_SUCCESS;