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>
17 #include <arch/acpi.h>
18 #include <amdblocks/BiosCallOuts.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."
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
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
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
;
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
98 while (CurrNodeOffset
!= 0) {
99 CurrNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
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
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
119 if (FreedNodePtr
->BufferSize
>=
120 (AllocParams
->BufferLength
+
121 sizeof(BIOS_BUFFER_NODE
))) {
122 if (BestFitNodeOffset
== 0) {
124 * First node that fits the requested
127 BestFitNodeOffset
= FreedNodeOffset
;
128 BestFitPrevNodeOffset
= PrevNodeOffset
;
131 * Find out whether current node is a
132 * betterfit than the previous nodes
134 BestFitNodePtr
= (BIOS_BUFFER_NODE
*)
137 if (BestFitNodePtr
->BufferSize
>
138 FreedNodePtr
->BufferSize
) {
142 BestFitPrevNodeOffset
=
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
+
175 NextFreePtr
->BufferSize
= BestFitNodePtr
->BufferSize
-
176 (AllocParams
->BufferLength
+
177 sizeof(BIOS_BUFFER_NODE
));
178 NextFreePtr
->NextNodeOffset
=
179 BestFitNodePtr
->NextNodeOffset
;
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
;
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
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
));
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
;
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)
297 NextNodeOffset
= NextNodePtr
->NextNodeOffset
;
298 NextNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
302 /* If deallocated node is adjacent to the next node,
303 * concatenate both nodes.
305 if (NextNodeOffset
== EndNodeOffset
) {
306 NextNodePtr
= (BIOS_BUFFER_NODE
*)(BiosHeapBaseAddr
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
));
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
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
));
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
+
373 AllocParams
->BufferPointer
= (UINT8
*)((UINT8
*)AllocNodePtr
374 + sizeof(BIOS_BUFFER_NODE
));
375 AllocParams
->BufferLength
= AllocNodePtr
->BufferSize
;
377 return AGESA_SUCCESS
;