1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <amdblocks/agesawrapper.h>
4 #include <amdblocks/BiosCallOuts.h>
8 static void *agesa_heap_base(void)
10 return cbmem_add(CBMEM_ID_RESUME_SCRATCH
, BIOS_HEAP_SIZE
);
13 static void EmptyHeap(int unused
)
15 void *BiosManagerPtr
= agesa_heap_base();
16 memset(BiosManagerPtr
, 0, BIOS_HEAP_SIZE
);
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.
29 * ConfigPtr Pointer to type AGESA_TEMP_HEAP_BASE_PARAMS
31 * Status Indicates whether TempHeapAddress was successfully
34 AGESA_STATUS
agesa_GetTempHeapBase(uint32_t Func
, uintptr_t Data
,
37 AGESA_TEMP_HEAP_BASE_PARAMS
*pTempHeapBase
;
39 pTempHeapBase
= (AGESA_TEMP_HEAP_BASE_PARAMS
*)ConfigPtr
;
40 pTempHeapBase
->TempHeapAddress
= CONFIG_PI_AGESA_TEMP_RAM_BASE
;
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.
53 * ConfigPtr Pointer to type AGESA_REBASE_PARAMS
55 * Status Indicates whether HeapAddress was successfully
58 AGESA_STATUS
agesa_HeapRebase(uint32_t Func
, uintptr_t Data
, void *ConfigPtr
)
60 AGESA_REBASE_PARAMS
*Rebase
;
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
;
71 * Name FindAllocatedNode
72 * Brief description Find an allocated node that matches the handle.
73 * Input parameter The desired handle.
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.
82 static AGESA_STATUS
FindAllocatedNode(uint32_t handle
,
83 BIOS_BUFFER_NODE
**last_allocd_or_match
)
85 uint32_t AllocNodeOffset
;
86 uint8_t *BiosHeapBaseAddr
;
87 BIOS_BUFFER_NODE
*AllocNodePtr
;
88 BIOS_HEAP_MANAGER
*BiosHeapBasePtr
;
89 AGESA_STATUS Status
= AGESA_SUCCESS
;
91 BiosHeapBaseAddr
= agesa_heap_base();
92 BiosHeapBasePtr
= (BIOS_HEAP_MANAGER
*)BiosHeapBaseAddr
;
94 AllocNodeOffset
= BiosHeapBasePtr
->StartOfAllocatedNodes
;
95 AllocNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+ AllocNodeOffset
);
97 while (handle
!= AllocNodePtr
->BufferHandle
) {
98 if (AllocNodePtr
->NextNodeOffset
== 0) {
99 Status
= AGESA_BOUNDS_CHK
;
102 AllocNodeOffset
= AllocNodePtr
->NextNodeOffset
;
103 AllocNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+
106 *last_allocd_or_match
= AllocNodePtr
;
111 * Name ConcatenateNodes
112 * Brief description Concatenates two adjacent nodes into a single node,
113 * this procedure is used by agesa_DeallocateBuffer().
115 * FirstNodePtr This node is in the front, its header will be
117 * SecondNodePtr This node is in the back, its header will be cleared.
119 static void ConcatenateNodes(BIOS_BUFFER_NODE
*FirstNodePtr
,
120 BIOS_BUFFER_NODE
*SecondNodePtr
)
122 FirstNodePtr
->BufferSize
+= SecondNodePtr
->BufferSize
+
123 sizeof(BIOS_BUFFER_NODE
);
124 FirstNodePtr
->NextNodeOffset
= SecondNodePtr
->NextNodeOffset
;
126 /* Zero out the SecondNode header */
127 memset(SecondNodePtr
, 0, sizeof(BIOS_BUFFER_NODE
));
130 CBMEM_CREATION_HOOK(EmptyHeap
);
132 AGESA_STATUS
agesa_AllocateBuffer(uint32_t Func
, uintptr_t Data
,
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
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
;
167 AllocParams
= ((AGESA_BUFFER_PARAMS
*)ConfigPtr
);
168 AllocParams
->BufferPointer
= NULL
;
169 MinimumSize
= AllocParams
->BufferLength
+ sizeof(BIOS_BUFFER_NODE
);
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
;
176 if (BiosHeapBasePtr
->StartOfAllocatedNodes
== 0) {
177 /* First allocation */
178 CurrNodeOffset
= sizeof(BIOS_HEAP_MANAGER
);
179 CurrNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
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
);
187 /* Update the remaining free space */
188 FreedNodeOffset
= CurrNodeOffset
+ CurrNodePtr
->BufferSize
189 + sizeof(BIOS_BUFFER_NODE
);
190 FreedNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
192 FreedNodePtr
->BufferSize
= AvailableHeapSize
193 - (FreedNodeOffset
- CurrNodeOffset
)
194 - sizeof(BIOS_BUFFER_NODE
);
195 FreedNodePtr
->NextNodeOffset
= 0;
197 /* Update the offsets for Allocated and Freed nodes */
198 BiosHeapBasePtr
->StartOfAllocatedNodes
= CurrNodeOffset
;
199 BiosHeapBasePtr
->StartOfFreedNodes
= FreedNodeOffset
;
202 * Find out whether BufferHandle has been allocated on the heap.
203 * If it has, return AGESA_BOUNDS_CHK.
205 Status
= FindAllocatedNode(AllocParams
->BufferHandle
,
207 if (Status
== AGESA_SUCCESS
)
208 return AGESA_BOUNDS_CHK
;
211 * If status ditn't returned AGESA_SUCCESS, CurrNodePtr here
212 * points to the end of the allocated nodes list.
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
223 FreedNodeSize
= FreedNodePtr
->BufferSize
;
224 if (FreedNodeSize
>= MinimumSize
) {
225 if (BestFitNodeOffset
== 0) {
227 * First node that fits the requested
230 BestFitNodeOffset
= FreedNodeOffset
;
231 BestFitPrevNodeOffset
= PrevNodeOffset
;
232 BestFitNodeSize
= FreedNodeSize
;
235 * Find out whether current node is a
236 * betterfit than the previous nodes
238 if (BestFitNodeSize
> FreedNodeSize
) {
241 BestFitPrevNodeOffset
=
243 BestFitNodeSize
= FreedNodeSize
;
247 PrevNodeOffset
= FreedNodeOffset
;
248 FreedNodeOffset
= FreedNodePtr
->NextNodeOffset
;
249 } /* end of while loop */
251 if (BestFitNodeOffset
== 0) {
253 * If we could not find a node that fits the requested
254 * buffer size, return AGESA_BOUNDS_CHK.
256 return AGESA_BOUNDS_CHK
;
259 BestFitNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
260 + BestFitNodeOffset
);
261 BestFitPrevNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+
262 BestFitPrevNodeOffset
);
265 * If BestFitNode is larger than the requested buffer,
266 * fragment the node further
268 if (BestFitNodePtr
->BufferSize
> MinimumSize
) {
269 NextFreeOffset
= BestFitNodeOffset
+ MinimumSize
;
270 NextFreePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+
272 NextFreePtr
->BufferSize
= BestFitNodeSize
- MinimumSize
;
274 /* Remove BestFitNode from list of Freed nodes */
275 NextFreePtr
->NextNodeOffset
=
276 BestFitNodePtr
->NextNodeOffset
;
279 * Otherwise, next free node is NextNodeOffset of
280 * BestFitNode. Remove it from list of Freed nodes.
282 NextFreeOffset
= BestFitNodePtr
->NextNodeOffset
;
286 * If BestFitNode is the first buffer in the list, then
287 * update StartOfFreedNodes to reflect new free node.
289 if (BestFitNodeOffset
== BiosHeapBasePtr
->StartOfFreedNodes
)
290 BiosHeapBasePtr
->StartOfFreedNodes
= NextFreeOffset
;
292 BestFitPrevNodePtr
->NextNodeOffset
= NextFreeOffset
;
294 /* Add BestFitNode to the list of Allocated nodes */
295 CurrNodePtr
->NextNodeOffset
= BestFitNodeOffset
;
296 BestFitNodePtr
->BufferSize
= AllocParams
->BufferLength
;
297 BestFitNodePtr
->BufferHandle
= AllocParams
->BufferHandle
;
298 BestFitNodePtr
->NextNodeOffset
= 0;
300 AllocParams
->BufferPointer
= (uint8_t *)BestFitNodePtr
+
301 sizeof(BIOS_BUFFER_NODE
);
304 return AGESA_SUCCESS
;
307 AGESA_STATUS
agesa_DeallocateBuffer(uint32_t Func
, uintptr_t Data
,
310 uint8_t *BiosHeapBaseAddr
;
311 uint32_t AllocNodeOffset
;
312 uint32_t PrevNodeOffset
;
313 uint32_t NextNodeOffset
;
314 uint32_t FreedNodeOffset
;
315 uint32_t EndNodeOffset
;
316 BIOS_BUFFER_NODE
*AllocNodePtr
;
317 BIOS_BUFFER_NODE
*PrevNodePtr
;
318 BIOS_BUFFER_NODE
*FreedNodePtr
;
319 BIOS_BUFFER_NODE
*NextNodePtr
;
320 BIOS_HEAP_MANAGER
*BiosHeapBasePtr
;
321 AGESA_BUFFER_PARAMS
*AllocParams
;
323 AllocParams
= (AGESA_BUFFER_PARAMS
*)ConfigPtr
;
325 BiosHeapBaseAddr
= agesa_heap_base();
326 BiosHeapBasePtr
= (BIOS_HEAP_MANAGER
*)BiosHeapBaseAddr
;
328 /* Find target node to deallocate in list of allocated nodes.
329 * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
331 AllocNodeOffset
= BiosHeapBasePtr
->StartOfAllocatedNodes
;
332 AllocNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+ AllocNodeOffset
);
333 PrevNodeOffset
= AllocNodeOffset
;
335 while (AllocNodePtr
->BufferHandle
!= AllocParams
->BufferHandle
) {
336 if (AllocNodePtr
->NextNodeOffset
== 0)
337 return AGESA_BOUNDS_CHK
;
338 PrevNodeOffset
= AllocNodeOffset
;
339 AllocNodeOffset
= AllocNodePtr
->NextNodeOffset
;
340 AllocNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
344 /* Remove target node from list of allocated nodes */
345 PrevNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+ PrevNodeOffset
);
346 PrevNodePtr
->NextNodeOffset
= AllocNodePtr
->NextNodeOffset
;
348 /* Zero out the buffer, and clear the BufferHandle */
349 memset((uint8_t *)AllocNodePtr
+ sizeof(BIOS_BUFFER_NODE
), 0,
350 AllocNodePtr
->BufferSize
);
351 AllocNodePtr
->BufferHandle
= 0;
353 /* Add deallocated node in order to the list of freed nodes */
354 FreedNodeOffset
= BiosHeapBasePtr
->StartOfFreedNodes
;
355 FreedNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
+ FreedNodeOffset
);
357 EndNodeOffset
= AllocNodeOffset
+ AllocNodePtr
->BufferSize
+
358 sizeof(BIOS_BUFFER_NODE
);
360 if (AllocNodeOffset
< FreedNodeOffset
) {
361 /* Add to the start of the freed list */
362 if (EndNodeOffset
== FreedNodeOffset
) {
363 /* If the freed node is adjacent to the first node in
364 * the list, concatenate both nodes
366 ConcatenateNodes(AllocNodePtr
, FreedNodePtr
);
368 /* Otherwise, add freed node to the start of the list
369 * Update NextNodeOffset and BufferSize to include the
370 * size of BIOS_BUFFER_NODE.
372 AllocNodePtr
->NextNodeOffset
= FreedNodeOffset
;
374 /* Update StartOfFreedNodes to the new first node */
375 BiosHeapBasePtr
->StartOfFreedNodes
= AllocNodeOffset
;
377 /* Traverse list of freed nodes to find where the deallocated
378 * node should be placed.
380 NextNodeOffset
= FreedNodeOffset
;
381 NextNodePtr
= FreedNodePtr
;
382 while (AllocNodeOffset
> NextNodeOffset
) {
383 PrevNodeOffset
= NextNodeOffset
;
384 if (NextNodePtr
->NextNodeOffset
== 0)
386 NextNodeOffset
= NextNodePtr
->NextNodeOffset
;
387 NextNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
391 /* If deallocated node is adjacent to the next node,
392 * concatenate both nodes.
394 if (NextNodeOffset
== EndNodeOffset
) {
395 NextNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
397 ConcatenateNodes(AllocNodePtr
, NextNodePtr
);
399 /*AllocNodePtr->NextNodeOffset =
400 * FreedNodePtr->NextNodeOffset; */
401 AllocNodePtr
->NextNodeOffset
= NextNodeOffset
;
404 * If deallocated node is adjacent to the previous node,
405 * concatenate both nodes.
407 PrevNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
409 EndNodeOffset
= PrevNodeOffset
+ PrevNodePtr
->BufferSize
+
410 sizeof(BIOS_BUFFER_NODE
);
412 if (AllocNodeOffset
== EndNodeOffset
)
413 ConcatenateNodes(PrevNodePtr
, AllocNodePtr
);
415 PrevNodePtr
->NextNodeOffset
= AllocNodeOffset
;
417 return AGESA_SUCCESS
;
420 AGESA_STATUS
agesa_LocateBuffer(uint32_t Func
, uintptr_t Data
, void *ConfigPtr
)
422 BIOS_BUFFER_NODE
*AllocNodePtr
;
423 AGESA_BUFFER_PARAMS
*AllocParams
;
426 AllocParams
= (AGESA_BUFFER_PARAMS
*)ConfigPtr
;
428 Status
= FindAllocatedNode(AllocParams
->BufferHandle
, &AllocNodePtr
);
430 if (Status
== AGESA_SUCCESS
) {
431 AllocParams
->BufferPointer
= (uint8_t *)((uint8_t *)AllocNodePtr
432 + sizeof(BIOS_BUFFER_NODE
));
433 AllocParams
->BufferLength
= AllocNodePtr
->BufferSize
;