mb/google/brya: Create rull variant
[coreboot2.git] / src / drivers / amd / agesa / heapmanager.c
blob8a82efedbadea6c87801f40be3f511614882c830
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #pragma pack(push)
4 #include <AGESA.h>
5 #pragma pack(pop)
7 #include <amdlib.h>
9 #include <cbmem.h>
10 #include <northbridge/amd/agesa/agesa_helper.h>
11 #include <northbridge/amd/agesa/BiosCallOuts.h>
13 #include <acpi/acpi.h>
14 #include <console/console.h>
15 #include <string.h>
17 /* BIOS_HEAP_START_ADDRESS is only for cold boots. */
18 #define BIOS_HEAP_SIZE 0x30000
19 #define BIOS_HEAP_START_ADDRESS 0x010000000
21 #if CONFIG(HAVE_ACPI_RESUME) && (HIGH_MEMORY_SCRATCH < BIOS_HEAP_SIZE)
22 #error Increase HIGH_MEMORY_SCRATCH allocation
23 #endif
25 void *GetHeapBase(void)
27 void *heap = (void *)BIOS_HEAP_START_ADDRESS;
29 if (acpi_is_wakeup_s3()) {
30 /* FIXME: For S3 resume path, buffer is in CBMEM
31 * with some arbitrary header. */
32 heap = cbmem_find(CBMEM_ID_RESUME_SCRATCH);
33 heap += 0x10;
36 return heap;
39 void EmptyHeap(void)
41 void *base = GetHeapBase();
42 memset(base, 0, BIOS_HEAP_SIZE);
44 printk(BIOS_DEBUG, "Wiped HEAP at [%08x - %08x]\n",
45 (unsigned int)(uintptr_t)base, (unsigned int)(uintptr_t)base + BIOS_HEAP_SIZE - 1);
48 #if defined(HEAP_CALLOUT_RUNTIME) && ENV_RAMSTAGE
50 #define AGESA_RUNTIME_SIZE 4096
51 static AGESA_STATUS alloc_cbmem(AGESA_BUFFER_PARAMS *AllocParams)
53 static unsigned int used = 0;
54 void *p = cbmem_find(CBMEM_ID_AGESA_RUNTIME);
56 if ((AGESA_RUNTIME_SIZE - used) < AllocParams->BufferLength) {
57 return AGESA_BOUNDS_CHK;
60 /* first time allocation */
61 if (!p) {
62 p = cbmem_add(CBMEM_ID_AGESA_RUNTIME, AGESA_RUNTIME_SIZE);
63 if (!p)
64 return AGESA_BOUNDS_CHK;
67 AllocParams->BufferPointer = p + used;
68 used += AllocParams->BufferLength;
69 return AGESA_SUCCESS;
71 #endif
73 typedef struct _BIOS_HEAP_MANAGER {
74 UINT32 StartOfAllocatedNodes;
75 UINT32 StartOfFreedNodes;
76 } BIOS_HEAP_MANAGER;
78 typedef struct _BIOS_BUFFER_NODE {
79 UINT32 BufferHandle;
80 UINT32 BufferSize;
81 UINT32 NextNodeOffset;
82 } BIOS_BUFFER_NODE;
84 static AGESA_STATUS agesa_AllocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr,
85 AGESA_BUFFER_PARAMS *AllocParams)
87 UINT32 AvailableHeapSize;
88 UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
89 UINT32 CurrNodeOffset;
90 UINT32 PrevNodeOffset;
91 UINT32 FreedNodeOffset;
92 UINT32 BestFitNodeOffset;
93 UINT32 BestFitPrevNodeOffset;
94 UINT32 NextFreeOffset;
95 BIOS_BUFFER_NODE *CurrNodePtr;
96 BIOS_BUFFER_NODE *FreedNodePtr;
97 BIOS_BUFFER_NODE *BestFitNodePtr;
98 BIOS_BUFFER_NODE *BestFitPrevNodePtr;
99 BIOS_BUFFER_NODE *NextFreePtr;
101 AllocParams->BufferPointer = NULL;
102 AvailableHeapSize = BIOS_HEAP_SIZE - sizeof(BIOS_HEAP_MANAGER);
104 if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
105 /* First allocation */
106 CurrNodeOffset = sizeof(BIOS_HEAP_MANAGER);
107 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + CurrNodeOffset);
108 CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
109 CurrNodePtr->BufferSize = AllocParams->BufferLength;
110 CurrNodePtr->NextNodeOffset = 0;
111 AllocParams->BufferPointer = (UINT8 *)CurrNodePtr + sizeof(BIOS_BUFFER_NODE);
113 /* Update the remaining free space */
114 FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof(BIOS_BUFFER_NODE);
115 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
116 FreedNodePtr->BufferSize = AvailableHeapSize
117 - (FreedNodeOffset - CurrNodeOffset)
118 - sizeof(BIOS_BUFFER_NODE);
119 FreedNodePtr->NextNodeOffset = 0;
121 /* Update the offsets for Allocated and Freed nodes */
122 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
123 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
124 } else {
125 /* Find out whether BufferHandle has been allocated on the heap.
126 * If it has, return AGESA_BOUNDS_CHK.
128 CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
129 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + CurrNodeOffset);
131 while (CurrNodeOffset != 0) {
132 CurrNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + CurrNodeOffset);
133 if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
134 return AGESA_BOUNDS_CHK;
136 CurrNodeOffset = CurrNodePtr->NextNodeOffset;
137 /* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
138 * to the end of the allocated nodes list.
141 /* Find the node that best fits the requested buffer size */
142 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
143 PrevNodeOffset = FreedNodeOffset;
144 BestFitNodeOffset = 0;
145 BestFitPrevNodeOffset = 0;
146 while (FreedNodeOffset != 0) {
147 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
148 if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE))) {
149 if (BestFitNodeOffset == 0) {
150 /* First node that fits the requested buffer size */
151 BestFitNodeOffset = FreedNodeOffset;
152 BestFitPrevNodeOffset = PrevNodeOffset;
153 } else {
154 /* Find out whether current node is a better fit than the previous nodes */
155 BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + BestFitNodeOffset);
156 if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
157 BestFitNodeOffset = FreedNodeOffset;
158 BestFitPrevNodeOffset = PrevNodeOffset;
162 PrevNodeOffset = FreedNodeOffset;
163 FreedNodeOffset = FreedNodePtr->NextNodeOffset;
164 } /* end of while loop */
166 if (BestFitNodeOffset == 0) {
167 /* If we could not find a node that fits the requested buffer
168 * size, return AGESA_BOUNDS_CHK.
170 return AGESA_BOUNDS_CHK;
171 } else {
172 BestFitNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + BestFitNodeOffset);
173 BestFitPrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + BestFitPrevNodeOffset);
175 /* If BestFitNode is larger than the requested buffer, fragment the node further */
176 if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE))) {
177 NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE);
179 NextFreePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + NextFreeOffset);
180 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof(BIOS_BUFFER_NODE));
181 NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
182 } else {
183 /* Otherwise, next free node is NextNodeOffset of BestFitNode */
184 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
187 /* If BestFitNode is the first buffer in the list, then update
188 * StartOfFreedNodes to reflect the new free node.
190 if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
191 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
192 } else {
193 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
196 /* Add BestFitNode to the list of Allocated nodes */
197 CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
198 BestFitNodePtr->BufferSize = AllocParams->BufferLength;
199 BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
200 BestFitNodePtr->NextNodeOffset = 0;
202 /* Remove BestFitNode from list of Freed nodes */
203 AllocParams->BufferPointer = (UINT8 *)BestFitNodePtr + sizeof(BIOS_BUFFER_NODE);
207 return AGESA_SUCCESS;
210 static AGESA_STATUS agesa_DeallocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr,
211 AGESA_BUFFER_PARAMS *AllocParams)
213 UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
214 UINT32 AllocNodeOffset;
215 UINT32 PrevNodeOffset;
216 UINT32 NextNodeOffset;
217 UINT32 FreedNodeOffset;
218 UINT32 EndNodeOffset;
219 BIOS_BUFFER_NODE *AllocNodePtr;
220 BIOS_BUFFER_NODE *PrevNodePtr;
221 BIOS_BUFFER_NODE *FreedNodePtr;
222 BIOS_BUFFER_NODE *NextNodePtr;
224 /* Find target node to deallocate in list of allocated nodes.
225 * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
227 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
228 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
229 PrevNodeOffset = AllocNodeOffset;
231 while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
232 if (AllocNodePtr->NextNodeOffset == 0) {
233 return AGESA_BOUNDS_CHK;
235 PrevNodeOffset = AllocNodeOffset;
236 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
237 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
240 /* Remove target node from list of allocated nodes */
241 PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
242 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
244 /* Zero out the buffer, and clear the BufferHandle */
245 LibAmdMemFill((UINT8 *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
246 AllocNodePtr->BufferHandle = 0;
248 /* Add deallocated node in order to the list of freed nodes */
249 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
250 FreedNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + FreedNodeOffset);
252 EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize +
253 sizeof(BIOS_BUFFER_NODE);
255 if (AllocNodeOffset < FreedNodeOffset) {
256 /* Add to the start of the freed list */
257 if (EndNodeOffset == FreedNodeOffset) {
258 /* If the freed node is adjacent to the first node in the list, concatenate both nodes */
259 AllocNodePtr->BufferSize += FreedNodePtr->BufferSize +
260 sizeof(BIOS_BUFFER_NODE);
261 AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;
263 /* Zero out the FreedNode header */
264 memset((UINT8 *)FreedNodePtr, 0,
265 sizeof(BIOS_BUFFER_NODE));
266 } else {
267 /* Otherwise, add freed node to the start of the list
268 * Update NextNodeOffset and BufferSize to include the
269 * size of BIOS_BUFFER_NODE.
271 AllocNodePtr->NextNodeOffset = FreedNodeOffset;
273 /* Update StartOfFreedNodes to the new first node */
274 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
275 } else {
276 /* Traverse list of freed nodes to find where the deallocated node
277 * should be placed.
279 NextNodeOffset = FreedNodeOffset;
280 NextNodePtr = FreedNodePtr;
281 while (AllocNodeOffset > NextNodeOffset) {
282 PrevNodeOffset = NextNodeOffset;
283 if (NextNodePtr->NextNodeOffset == 0) {
284 break;
286 NextNodeOffset = NextNodePtr->NextNodeOffset;
287 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + NextNodeOffset);
290 /* If deallocated node is adjacent to the next node,
291 * concatenate both nodes.
293 if (NextNodeOffset == EndNodeOffset) {
294 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + NextNodeOffset);
295 AllocNodePtr->BufferSize += NextNodePtr->BufferSize +
296 sizeof(BIOS_BUFFER_NODE);
297 AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
299 /* Zero out the NextNode header */
300 memset((UINT8 *)NextNodePtr, 0,
301 sizeof(BIOS_BUFFER_NODE));
302 } else {
303 /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
304 AllocNodePtr->NextNodeOffset = NextNodeOffset;
306 /* If deallocated node is adjacent to the previous node,
307 * concatenate both nodes.
309 PrevNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + PrevNodeOffset);
310 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
311 sizeof(BIOS_BUFFER_NODE);
312 if (AllocNodeOffset == EndNodeOffset) {
313 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
314 PrevNodePtr->BufferSize += AllocNodePtr->BufferSize +
315 sizeof(BIOS_BUFFER_NODE);
317 /* Zero out the AllocNode header */
318 memset((UINT8 *)AllocNodePtr, 0,
319 sizeof(BIOS_BUFFER_NODE));
320 } else {
321 PrevNodePtr->NextNodeOffset = AllocNodeOffset;
324 return AGESA_SUCCESS;
327 static AGESA_STATUS agesa_LocateBuffer(BIOS_HEAP_MANAGER *BiosHeapBasePtr,
328 AGESA_BUFFER_PARAMS *AllocParams)
330 UINT32 AllocNodeOffset;
331 UINT8 *BiosHeapBaseAddr = (void *)BiosHeapBasePtr;
332 BIOS_BUFFER_NODE *AllocNodePtr;
334 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
335 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
337 while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
338 if (AllocNodePtr->NextNodeOffset == 0) {
339 AllocParams->BufferPointer = NULL;
340 AllocParams->BufferLength = 0;
341 return AGESA_BOUNDS_CHK;
342 } else {
343 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
344 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr + AllocNodeOffset);
348 AllocParams->BufferPointer = (UINT8 *)((UINT8 *)AllocNodePtr + sizeof(BIOS_BUFFER_NODE));
349 AllocParams->BufferLength = AllocNodePtr->BufferSize;
351 return AGESA_SUCCESS;
354 AGESA_STATUS HeapManagerCallout(UINT32 Func, UINTN Data, VOID *ConfigPtr)
356 AGESA_BUFFER_PARAMS *AllocParams = ConfigPtr;
358 #if defined(HEAP_CALLOUT_RUNTIME) && ENV_RAMSTAGE
359 if (Func == AGESA_ALLOCATE_BUFFER && Data == HEAP_CALLOUT_RUNTIME)
360 return alloc_cbmem(AllocParams);
361 #endif
363 /* Must not call GetHeapBase() in AGESA_UNSUPPORTED path. */
364 if (Func == AGESA_LOCATE_BUFFER)
365 return agesa_LocateBuffer(GetHeapBase(), AllocParams);
366 else if (Func == AGESA_ALLOCATE_BUFFER)
367 return agesa_AllocateBuffer(GetHeapBase(), AllocParams);
368 else if (Func == AGESA_DEALLOCATE_BUFFER)
369 return agesa_DeallocateBuffer(GetHeapBase(), AllocParams);
371 return AGESA_UNSUPPORTED;