blob: e6f1e5f508b488ae9d61fa21e7659586096e3797 [file] [log] [blame]
Kyösti Mälkkief9343c2014-05-04 11:42:55 +03001
Kyösti Mälkki526c2fb2014-07-10 22:16:58 +03002#include "AGESA.h"
Kyösti Mälkkief9343c2014-05-04 11:42:55 +03003#include "amdlib.h"
Kyösti Mälkkief9343c2014-05-04 11:42:55 +03004#include "heapManager.h"
5
6#include <cbmem.h>
Kyösti Mälkkif7284082015-05-26 10:12:45 +03007#include <cpu/amd/agesa/s3_resume.h>
8#include <northbridge/amd/agesa/BiosCallOuts.h>
9
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030010#include <arch/acpi.h>
11#include <string.h>
12
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030013#if IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN) || \
14 IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_RL) || \
15 IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY16_KB)
16
17/* BIOS_HEAP_START_ADDRESS is only for cold boots. */
18#define BIOS_HEAP_SIZE 0x30000
19#define BIOS_HEAP_START_ADDRESS 0x010000000
20
21#else
22
23/* BIOS_HEAP_START_ADDRESS is only for cold boots. */
24#define BIOS_HEAP_SIZE 0x20000
25#define BIOS_HEAP_START_ADDRESS (BSP_STACK_BASE_ADDR - BIOS_HEAP_SIZE)
26
27#endif
28
Kyösti Mälkkif7284082015-05-26 10:12:45 +030029#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && (HIGH_MEMORY_SCRATCH < BIOS_HEAP_SIZE)
30#error Increase HIGH_MEMORY_SCRATCH allocation
31#endif
32
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030033void *GetHeapBase(void)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030034{
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030035 void *heap = (void *)BIOS_HEAP_START_ADDRESS;
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030036
Kyösti Mälkki78c5d582015-01-09 23:48:47 +020037 if (acpi_is_wakeup_s3())
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030038 heap = cbmem_find(CBMEM_ID_RESUME_SCRATCH);
Kyösti Mälkki78c5d582015-01-09 23:48:47 +020039
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030040 return heap;
41}
42
43void EmptyHeap(void)
44{
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030045 void *base = GetHeapBase();
46 memset(base, 0, BIOS_HEAP_SIZE);
47}
48
49void ResumeHeap(void **heap, size_t *len)
50{
51 void *base = GetHeapBase();
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +030052 *heap = base;
53 *len = BIOS_HEAP_SIZE;
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030054}
55
Dave Frodinb85656f2015-02-21 08:22:51 -070056#if (IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN) || \
57 IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_RL)) && !defined(__PRE_RAM__)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030058
59#define AGESA_RUNTIME_SIZE 4096
60
61static AGESA_STATUS alloc_cbmem(AGESA_BUFFER_PARAMS *AllocParams) {
62 static unsigned int used = 0;
63 void *p = cbmem_find(CBMEM_ID_AGESA_RUNTIME);
64
65 if ((AGESA_RUNTIME_SIZE - used) < AllocParams->BufferLength) {
66 return AGESA_BOUNDS_CHK;
67 }
68
69 /* first time allocation */
70 if (!p) {
71 p = cbmem_add(CBMEM_ID_AGESA_RUNTIME, AGESA_RUNTIME_SIZE);
72 if (!p)
73 return AGESA_BOUNDS_CHK;
74 }
75
76 AllocParams->BufferPointer = p + used;
77 used += AllocParams->BufferLength;
78 return AGESA_SUCCESS;
79}
80#endif
81
Kyösti Mälkki82fbda72015-01-02 22:46:32 +020082typedef struct _BIOS_HEAP_MANAGER {
83 UINT32 StartOfAllocatedNodes;
84 UINT32 StartOfFreedNodes;
85} BIOS_HEAP_MANAGER;
86
87typedef struct _BIOS_BUFFER_NODE {
88 UINT32 BufferHandle;
89 UINT32 BufferSize;
90 UINT32 NextNodeOffset;
91} BIOS_BUFFER_NODE;
92
93static AGESA_STATUS agesa_AllocateBuffer(UINT32 Func, UINT32 Data, VOID *ConfigPtr)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +030094{
95 UINT32 AvailableHeapSize;
96 UINT8 *BiosHeapBaseAddr;
97 UINT32 CurrNodeOffset;
98 UINT32 PrevNodeOffset;
99 UINT32 FreedNodeOffset;
100 UINT32 BestFitNodeOffset;
101 UINT32 BestFitPrevNodeOffset;
102 UINT32 NextFreeOffset;
103 BIOS_BUFFER_NODE *CurrNodePtr;
104 BIOS_BUFFER_NODE *FreedNodePtr;
105 BIOS_BUFFER_NODE *BestFitNodePtr;
106 BIOS_BUFFER_NODE *BestFitPrevNodePtr;
107 BIOS_BUFFER_NODE *NextFreePtr;
108 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
109 AGESA_BUFFER_PARAMS *AllocParams;
110
111 AllocParams = ((AGESA_BUFFER_PARAMS *) ConfigPtr);
112 AllocParams->BufferPointer = NULL;
113
Dave Frodinb85656f2015-02-21 08:22:51 -0700114#if (IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN) || \
115 IS_ENABLED(CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_RL)) && !defined(__PRE_RAM__)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300116 /* if the allocation is for runtime use simple CBMEM data */
117 if (Data == HEAP_CALLOUT_RUNTIME)
118 return alloc_cbmem(AllocParams);
119#endif
120
121 AvailableHeapSize = BIOS_HEAP_SIZE - sizeof (BIOS_HEAP_MANAGER);
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +0300122 BiosHeapBaseAddr = GetHeapBase();
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300123 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
124
125 if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
126 /* First allocation */
127 CurrNodeOffset = sizeof (BIOS_HEAP_MANAGER);
128 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
129 CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
130 CurrNodePtr->BufferSize = AllocParams->BufferLength;
131 CurrNodePtr->NextNodeOffset = 0;
132 AllocParams->BufferPointer = (UINT8 *) CurrNodePtr + sizeof (BIOS_BUFFER_NODE);
133
134 /* Update the remaining free space */
135 FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof (BIOS_BUFFER_NODE);
136 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
137 FreedNodePtr->BufferSize = AvailableHeapSize - sizeof (BIOS_BUFFER_NODE) - CurrNodePtr->BufferSize;
138 FreedNodePtr->NextNodeOffset = 0;
139
140 /* Update the offsets for Allocated and Freed nodes */
141 BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
142 BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
143 } else {
144 /* Find out whether BufferHandle has been allocated on the heap.
145 * If it has, return AGESA_BOUNDS_CHK.
146 */
147 CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
148 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
149
150 while (CurrNodeOffset != 0) {
151 CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
152 if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
153 return AGESA_BOUNDS_CHK;
154 }
155 CurrNodeOffset = CurrNodePtr->NextNodeOffset;
156 /* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
157 * to the end of the allocated nodes list.
158 */
159 }
160 /* Find the node that best fits the requested buffer size */
161 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
162 PrevNodeOffset = FreedNodeOffset;
163 BestFitNodeOffset = 0;
164 BestFitPrevNodeOffset = 0;
165 while (FreedNodeOffset != 0) {
166 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
167 if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
168 if (BestFitNodeOffset == 0) {
169 /* First node that fits the requested buffer size */
170 BestFitNodeOffset = FreedNodeOffset;
171 BestFitPrevNodeOffset = PrevNodeOffset;
172 } else {
173 /* Find out whether current node is a better fit than the previous nodes */
174 BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
175 if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
176 BestFitNodeOffset = FreedNodeOffset;
177 BestFitPrevNodeOffset = PrevNodeOffset;
178 }
179 }
180 }
181 PrevNodeOffset = FreedNodeOffset;
182 FreedNodeOffset = FreedNodePtr->NextNodeOffset;
183 } /* end of while loop */
184
185 if (BestFitNodeOffset == 0) {
186 /* If we could not find a node that fits the requested buffer
187 * size, return AGESA_BOUNDS_CHK.
188 */
189 return AGESA_BOUNDS_CHK;
190 } else {
191 BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
192 BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);
193
194 /* If BestFitNode is larger than the requested buffer, fragment the node further */
195 if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
196 NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE);
197
198 NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
199 NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE));
200 NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
201 } else {
202 /* Otherwise, next free node is NextNodeOffset of BestFitNode */
203 NextFreeOffset = BestFitNodePtr->NextNodeOffset;
204 }
205
206 /* If BestFitNode is the first buffer in the list, then update
207 * StartOfFreedNodes to reflect the new free node.
208 */
209 if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
210 BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
211 } else {
212 BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
213 }
214
215 /* Add BestFitNode to the list of Allocated nodes */
216 CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
217 BestFitNodePtr->BufferSize = AllocParams->BufferLength;
218 BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
219 BestFitNodePtr->NextNodeOffset = 0;
220
221 /* Remove BestFitNode from list of Freed nodes */
222 AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof (BIOS_BUFFER_NODE);
223 }
224 }
225
226 return AGESA_SUCCESS;
227}
228
Kyösti Mälkki82fbda72015-01-02 22:46:32 +0200229static AGESA_STATUS agesa_DeallocateBuffer(UINT32 Func, UINT32 Data, VOID *ConfigPtr)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300230{
231
232 UINT8 *BiosHeapBaseAddr;
233 UINT32 AllocNodeOffset;
234 UINT32 PrevNodeOffset;
235 UINT32 NextNodeOffset;
236 UINT32 FreedNodeOffset;
237 UINT32 EndNodeOffset;
238 BIOS_BUFFER_NODE *AllocNodePtr;
239 BIOS_BUFFER_NODE *PrevNodePtr;
240 BIOS_BUFFER_NODE *FreedNodePtr;
241 BIOS_BUFFER_NODE *NextNodePtr;
242 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
243 AGESA_BUFFER_PARAMS *AllocParams;
244
245 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
246
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +0300247 BiosHeapBaseAddr = GetHeapBase();
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300248 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
249
250 /* Find target node to deallocate in list of allocated nodes.
251 * Return AGESA_BOUNDS_CHK if the BufferHandle is not found.
252 */
253 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
254 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
255 PrevNodeOffset = AllocNodeOffset;
256
257 while (AllocNodePtr->BufferHandle != AllocParams->BufferHandle) {
258 if (AllocNodePtr->NextNodeOffset == 0) {
259 return AGESA_BOUNDS_CHK;
260 }
261 PrevNodeOffset = AllocNodeOffset;
262 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
263 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
264 }
265
266 /* Remove target node from list of allocated nodes */
267 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
268 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
269
270 /* Zero out the buffer, and clear the BufferHandle */
271 LibAmdMemFill ((UINT8 *)AllocNodePtr + sizeof (BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
272 AllocNodePtr->BufferHandle = 0;
273 AllocNodePtr->BufferSize += sizeof (BIOS_BUFFER_NODE);
274
275 /* Add deallocated node in order to the list of freed nodes */
276 FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
277 FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
278
279 EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize;
280
281 if (AllocNodeOffset < FreedNodeOffset) {
282 /* Add to the start of the freed list */
283 if (EndNodeOffset == FreedNodeOffset) {
284 /* If the freed node is adjacent to the first node in the list, concatenate both nodes */
285 AllocNodePtr->BufferSize += FreedNodePtr->BufferSize;
286 AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;
287
288 /* Clear the BufferSize and NextNodeOffset of the previous first node */
289 FreedNodePtr->BufferSize = 0;
290 FreedNodePtr->NextNodeOffset = 0;
291
292 } else {
293 /* Otherwise, add freed node to the start of the list
294 * Update NextNodeOffset and BufferSize to include the
295 * size of BIOS_BUFFER_NODE.
296 */
297 AllocNodePtr->NextNodeOffset = FreedNodeOffset;
298 }
299 /* Update StartOfFreedNodes to the new first node */
300 BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
301 } else {
302 /* Traverse list of freed nodes to find where the deallocated node
303 * should be placed.
304 */
305 NextNodeOffset = FreedNodeOffset;
306 NextNodePtr = FreedNodePtr;
307 while (AllocNodeOffset > NextNodeOffset) {
308 PrevNodeOffset = NextNodeOffset;
309 if (NextNodePtr->NextNodeOffset == 0) {
310 break;
311 }
312 NextNodeOffset = NextNodePtr->NextNodeOffset;
313 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
314 }
315
316 /* If deallocated node is adjacent to the next node,
317 * concatenate both nodes.
318 */
319 if (NextNodeOffset == EndNodeOffset) {
320 NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
321 AllocNodePtr->BufferSize += NextNodePtr->BufferSize;
322 AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;
323
324 NextNodePtr->BufferSize = 0;
325 NextNodePtr->NextNodeOffset = 0;
326 } else {
327 /*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
328 AllocNodePtr->NextNodeOffset = NextNodeOffset;
329 }
330 /* If deallocated node is adjacent to the previous node,
331 * concatenate both nodes.
332 */
333 PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
334 EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize;
335 if (AllocNodeOffset == EndNodeOffset) {
336 PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
337 PrevNodePtr->BufferSize += AllocNodePtr->BufferSize;
338
339 AllocNodePtr->BufferSize = 0;
340 AllocNodePtr->NextNodeOffset = 0;
341 } else {
342 PrevNodePtr->NextNodeOffset = AllocNodeOffset;
343 }
344 }
345 return AGESA_SUCCESS;
346}
347
Kyösti Mälkki82fbda72015-01-02 22:46:32 +0200348static AGESA_STATUS agesa_LocateBuffer(UINT32 Func, UINT32 Data, VOID *ConfigPtr)
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300349{
350 UINT32 AllocNodeOffset;
351 UINT8 *BiosHeapBaseAddr;
352 BIOS_BUFFER_NODE *AllocNodePtr;
353 BIOS_HEAP_MANAGER *BiosHeapBasePtr;
354 AGESA_BUFFER_PARAMS *AllocParams;
355
356 AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;
357
Kyösti Mälkkib1fcbf32015-05-26 11:25:13 +0300358 BiosHeapBaseAddr = GetHeapBase();
Kyösti Mälkkief9343c2014-05-04 11:42:55 +0300359 BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BiosHeapBaseAddr;
360
361 AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
362 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
363
364 while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
365 if (AllocNodePtr->NextNodeOffset == 0) {
366 AllocParams->BufferPointer = NULL;
367 AllocParams->BufferLength = 0;
368 return AGESA_BOUNDS_CHK;
369 } else {
370 AllocNodeOffset = AllocNodePtr->NextNodeOffset;
371 AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
372 }
373 }
374
375 AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof (BIOS_BUFFER_NODE));
376 AllocParams->BufferLength = AllocNodePtr->BufferSize;
377
378 return AGESA_SUCCESS;
379
380}
Kyösti Mälkki82fbda72015-01-02 22:46:32 +0200381
Stefan Reinauerdd132a52015-07-30 11:16:37 -0700382AGESA_STATUS HeapManagerCallout(UINT32 Func, UINTN Data, VOID *ConfigPtr)
Kyösti Mälkki82fbda72015-01-02 22:46:32 +0200383{
384 if (Func == AGESA_LOCATE_BUFFER)
385 return agesa_LocateBuffer(Func, Data, ConfigPtr);
386 else if (Func == AGESA_ALLOCATE_BUFFER)
387 return agesa_AllocateBuffer(Func, Data, ConfigPtr);
388 else if (Func == AGESA_DEALLOCATE_BUFFER)
389 return agesa_DeallocateBuffer(Func, Data, ConfigPtr);
390 else
391 return AGESA_UNSUPPORTED;
392}