drivers/usb/acpi: Don't add GPIOs to _CRS for Intel Bluetooth
[coreboot2.git] / src / soc / amd / common / pi / heapmanager.c
blob34a4837d73dc72cefc9e4a69364654810e551051
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <amdblocks/agesawrapper.h>
4 #include <amdblocks/BiosCallOuts.h>
5 #include <cbmem.h>
6 #include <string.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.
26 * Input parameters
27 * Func Unused
28 * Data Unused
29 * ConfigPtr Pointer to type AGESA_TEMP_HEAP_BASE_PARAMS
30 * Output parameters
31 * Status Indicates whether TempHeapAddress was successfully
32 * set.
34 AGESA_STATUS agesa_GetTempHeapBase(uint32_t Func, uintptr_t Data,
35 void *ConfigPtr)
37 AGESA_TEMP_HEAP_BASE_PARAMS *pTempHeapBase;
39 pTempHeapBase = (AGESA_TEMP_HEAP_BASE_PARAMS *)ConfigPtr;
40 pTempHeapBase->TempHeapAddress = CONFIG_PI_AGESA_TEMP_RAM_BASE;
42 return AGESA_SUCCESS;
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.
50 * Input parameters
51 * Func Unused
52 * Data Unused
53 * ConfigPtr Pointer to type AGESA_REBASE_PARAMS
54 * Output parameters
55 * Status Indicates whether HeapAddress was successfully
56 * set.
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;
67 return AGESA_SUCCESS;
71 * Name FindAllocatedNode
72 * Brief description Find an allocated node that matches the handle.
73 * Input parameter The desired handle.
74 * Output parameters
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;
100 break;
102 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
103 AllocNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr +
104 AllocNodeOffset);
106 *last_allocd_or_match = AllocNodePtr;
107 return Status;
111 * Name ConcatenateNodes
112 * Brief description Concatenates two adjacent nodes into a single node,
113 * this procedure is used by agesa_DeallocateBuffer().
114 * Input parameters
115 * FirstNodePtr This node is in the front, its header will be
116 * maintained.
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,
133 void *ConfigPtr)
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
145 * the best fit.
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;
165 AGESA_STATUS Status;
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
180 + CurrNodeOffset);
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
191 + FreedNodeOffset);
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;
200 } else {
202 * Find out whether BufferHandle has been allocated on the heap.
203 * If it has, return AGESA_BOUNDS_CHK.
205 Status = FindAllocatedNode(AllocParams->BufferHandle,
206 &CurrNodePtr);
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
222 + FreedNodeOffset);
223 FreedNodeSize = FreedNodePtr->BufferSize;
224 if (FreedNodeSize >= MinimumSize) {
225 if (BestFitNodeOffset == 0) {
227 * First node that fits the requested
228 * buffer size
230 BestFitNodeOffset = FreedNodeOffset;
231 BestFitPrevNodeOffset = PrevNodeOffset;
232 BestFitNodeSize = FreedNodeSize;
233 } else {
235 * Find out whether current node is a
236 * betterfit than the previous nodes
238 if (BestFitNodeSize > FreedNodeSize) {
239 BestFitNodeOffset =
240 FreedNodeOffset;
241 BestFitPrevNodeOffset =
242 PrevNodeOffset;
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 +
271 NextFreeOffset);
272 NextFreePtr->BufferSize = BestFitNodeSize - MinimumSize;
274 /* Remove BestFitNode from list of Freed nodes */
275 NextFreePtr->NextNodeOffset =
276 BestFitNodePtr->NextNodeOffset;
277 } else {
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;
291 else
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,
308 void *ConfigPtr)
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
341 + AllocNodeOffset);
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);
367 } else {
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;
376 } else {
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)
385 break;
386 NextNodeOffset = NextNodePtr->NextNodeOffset;
387 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
388 + NextNodeOffset);
391 /* If deallocated node is adjacent to the next node,
392 * concatenate both nodes.
394 if (NextNodeOffset == EndNodeOffset) {
395 NextNodePtr = (BIOS_BUFFER_NODE *)(BiosHeapBaseAddr
396 + NextNodeOffset);
397 ConcatenateNodes(AllocNodePtr, NextNodePtr);
398 } else {
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
408 + PrevNodeOffset);
409 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize +
410 sizeof(BIOS_BUFFER_NODE);
412 if (AllocNodeOffset == EndNodeOffset)
413 ConcatenateNodes(PrevNodePtr, AllocNodePtr);
414 else
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;
424 AGESA_STATUS Status;
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;
436 return Status;