]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Core/Dxe/Mem/Pool.c
Use TmpStr as a backup, as StrCpy in BaseLib does not handle copy of two strings
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Mem / Pool.c
CommitLineData
23c98c94 1/** @file\r
504214c4
LG
2 UEFI Memory pool management functions.\r
3\r
23c98c94 4Copyright (c) 2006 - 2008, Intel Corporation. <BR>\r
5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
28a00297 12\r
504214c4 13**/\r
28a00297 14\r
15#include <DxeMain.h>\r
16\r
17#define POOL_FREE_SIGNATURE EFI_SIGNATURE_32('p','f','r','0')\r
18typedef struct {\r
19 UINT32 Signature;\r
20 UINT32 Index;\r
21 LIST_ENTRY Link;\r
22} POOL_FREE;\r
23\r
24\r
25#define POOL_HEAD_SIGNATURE EFI_SIGNATURE_32('p','h','d','0')\r
26typedef struct {\r
27 UINT32 Signature;\r
28 UINT32 Size;\r
29 EFI_MEMORY_TYPE Type;\r
30 UINTN Reserved;\r
31 CHAR8 Data[1];\r
32} POOL_HEAD;\r
33\r
34#define SIZE_OF_POOL_HEAD EFI_FIELD_OFFSET(POOL_HEAD,Data)\r
35\r
36#define POOL_TAIL_SIGNATURE EFI_SIGNATURE_32('p','t','a','l')\r
37typedef struct {\r
38 UINT32 Signature;\r
39 UINT32 Size;\r
40} POOL_TAIL;\r
41\r
42\r
43#define POOL_SHIFT 7\r
44\r
45#define POOL_OVERHEAD (SIZE_OF_POOL_HEAD + sizeof(POOL_TAIL))\r
46\r
47#define HEAD_TO_TAIL(a) \\r
48 ((POOL_TAIL *) (((CHAR8 *) (a)) + (a)->Size - sizeof(POOL_TAIL)));\r
49\r
50\r
51#define SIZE_TO_LIST(a) ((a) >> POOL_SHIFT)\r
52#define LIST_TO_SIZE(a) ((a+1) << POOL_SHIFT)\r
53\r
54#define MAX_POOL_LIST SIZE_TO_LIST(DEFAULT_PAGE_ALLOCATION)\r
55\r
56#define MAX_POOL_SIZE (MAX_ADDRESS - POOL_OVERHEAD)\r
57\r
58//\r
59// Globals\r
60//\r
61\r
62#define POOL_SIGNATURE EFI_SIGNATURE_32('p','l','s','t')\r
63typedef struct {\r
64 INTN Signature;\r
65 UINTN Used;\r
66 EFI_MEMORY_TYPE MemoryType;\r
67 LIST_ENTRY FreeList[MAX_POOL_LIST];\r
68 LIST_ENTRY Link;\r
69} POOL; \r
70\r
28a00297 71//\r
e94a9ff7 72// Pool header for each memory type.\r
73//\r
74POOL mPoolHead[EfiMaxMemoryType];\r
75\r
28a00297 76//\r
e94a9ff7 77// List of pool header to search for the appropriate memory type.\r
28a00297 78//\r
e94a9ff7 79LIST_ENTRY mPoolHeadList;\r
28a00297 80\r
162ed594 81\r
82/**\r
83 Called to initialize the pool.\r
84\r
85**/\r
28a00297 86VOID\r
87CoreInitializePool (\r
88 VOID\r
89 )\r
28a00297 90{\r
91 UINTN Type;\r
92 UINTN Index;\r
93\r
94 for (Type=0; Type < EfiMaxMemoryType; Type++) {\r
e94a9ff7 95 mPoolHead[Type].Signature = 0;\r
96 mPoolHead[Type].Used = 0;\r
97 mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;\r
28a00297 98 for (Index=0; Index < MAX_POOL_LIST; Index++) {\r
e94a9ff7 99 InitializeListHead (&mPoolHead[Type].FreeList[Index]);\r
28a00297 100 }\r
101 }\r
e94a9ff7 102 InitializeListHead (&mPoolHeadList);\r
28a00297 103}\r
104\r
28a00297 105\r
162ed594 106/**\r
28a00297 107 Look up pool head for specified memory type.\r
108\r
162ed594 109 @param MemoryType Memory type of which pool head is looked for \r
28a00297 110\r
162ed594 111 @return Pointer of Corresponding pool head.\r
28a00297 112\r
162ed594 113**/\r
162ed594 114POOL *\r
115LookupPoolHead (\r
116 IN EFI_MEMORY_TYPE MemoryType\r
117 )\r
28a00297 118{\r
119 LIST_ENTRY *Link;\r
120 POOL *Pool;\r
121 UINTN Index;\r
122\r
123 if (MemoryType >= 0 && MemoryType < EfiMaxMemoryType) {\r
e94a9ff7 124 return &mPoolHead[MemoryType];\r
28a00297 125 }\r
126\r
127 if (MemoryType < 0) {\r
128\r
e94a9ff7 129 for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {\r
28a00297 130 Pool = CR(Link, POOL, Link, POOL_SIGNATURE);\r
131 if (Pool->MemoryType == MemoryType) {\r
132 return Pool;\r
133 }\r
134 }\r
135\r
136 Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL));\r
137 if (Pool == NULL) {\r
138 return NULL;\r
139 }\r
140\r
141 Pool->Signature = POOL_SIGNATURE;\r
142 Pool->Used = 0;\r
143 Pool->MemoryType = MemoryType;\r
144 for (Index=0; Index < MAX_POOL_LIST; Index++) {\r
145 InitializeListHead (&Pool->FreeList[Index]);\r
146 }\r
147\r
e94a9ff7 148 InsertHeadList (&mPoolHeadList, &Pool->Link);\r
28a00297 149\r
150 return Pool;\r
151 }\r
152\r
153 return NULL;\r
154}\r
155\r
156 \r
157\r
162ed594 158/**\r
159 Allocate pool of a particular type.\r
160\r
161 @param PoolType Type of pool to allocate \r
162 @param Size The amount of pool to allocate \r
163 @param Buffer The address to return a pointer to the allocated \r
164 pool \r
165\r
166 @retval EFI_INVALID_PARAMETER PoolType not valid \r
167 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. \r
168 @retval EFI_SUCCESS Pool successfully allocated.\r
169\r
170**/\r
28a00297 171EFI_STATUS\r
172EFIAPI\r
173CoreAllocatePool (\r
174 IN EFI_MEMORY_TYPE PoolType,\r
175 IN UINTN Size,\r
176 OUT VOID **Buffer\r
177 )\r
28a00297 178{\r
179 EFI_STATUS Status;\r
180\r
181 //\r
182 // If it's not a valid type, fail it\r
183 //\r
184 if ((PoolType >= EfiMaxMemoryType && PoolType <= 0x7fffffff) ||\r
185 PoolType == EfiConventionalMemory) {\r
186 return EFI_INVALID_PARAMETER;\r
187 }\r
188 \r
189 *Buffer = NULL;\r
190 \r
191 //\r
192 // If size is too large, fail it\r
193 // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES\r
194 //\r
195 if (Size > MAX_POOL_SIZE) {\r
196 return EFI_OUT_OF_RESOURCES;\r
197 }\r
198\r
199 //\r
200 // Acquire the memory lock and make the allocation\r
201 //\r
202 Status = CoreAcquireLockOrFail (&gMemoryLock);\r
203 if (EFI_ERROR (Status)) {\r
204 return EFI_OUT_OF_RESOURCES;\r
205 }\r
206\r
207 *Buffer = CoreAllocatePoolI (PoolType, Size);\r
208 CoreReleaseMemoryLock ();\r
209 return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;\r
210}\r
211\r
212\r
28a00297 213\r
162ed594 214/**\r
28a00297 215 Internal function to allocate pool of a particular type.\r
28a00297 216 Caller must have the memory lock held\r
217\r
162ed594 218 @param PoolType Type of pool to allocate \r
219 @param Size The amount of pool to allocate \r
28a00297 220\r
162ed594 221 @return The allocate pool, or NULL\r
28a00297 222\r
162ed594 223**/\r
224VOID *\r
225CoreAllocatePoolI (\r
226 IN EFI_MEMORY_TYPE PoolType,\r
227 IN UINTN Size\r
228 )\r
28a00297 229{\r
230 POOL *Pool;\r
231 POOL_FREE *Free;\r
232 POOL_HEAD *Head;\r
233 POOL_TAIL *Tail;\r
234 CHAR8 *NewPage;\r
235 VOID *Buffer;\r
236 UINTN Index;\r
237 UINTN FSize;\r
162ed594 238 UINTN Offset;\r
28a00297 239 UINTN Adjustment;\r
240 UINTN NoPages;\r
241\r
242 ASSERT_LOCKED (&gMemoryLock);\r
243\r
244 //\r
245 // Adjust the size by the pool header & tail overhead\r
246 //\r
247 \r
248 //\r
249 // Adjusting the Size to be of proper alignment so that\r
250 // we don't get an unaligned access fault later when\r
251 // pool_Tail is being initialized\r
252 //\r
253 ALIGN_VARIABLE (Size, Adjustment);\r
254\r
255 Size += POOL_OVERHEAD;\r
256 Index = SIZE_TO_LIST(Size);\r
257 Pool = LookupPoolHead (PoolType);\r
258 if (Pool== NULL) {\r
259 return NULL;\r
260 }\r
261 Head = NULL;\r
262\r
263 //\r
264 // If allocation is over max size, just allocate pages for the request\r
265 // (slow)\r
266 //\r
267 if (Index >= MAX_POOL_LIST) {\r
268 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1;\r
269 NoPages &= ~(EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1);\r
270 Head = CoreAllocatePoolPages (PoolType, NoPages, DEFAULT_PAGE_ALLOCATION);\r
271 goto Done;\r
272 }\r
273\r
274 //\r
275 // If there's no free pool in the proper list size, go get some more pages\r
276 //\r
277 if (IsListEmpty (&Pool->FreeList[Index])) {\r
278\r
279 //\r
280 // Get another page\r
281 //\r
282 NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION), DEFAULT_PAGE_ALLOCATION);\r
283 if (NewPage == NULL) {\r
284 goto Done;\r
285 }\r
286\r
287 //\r
288 // Carve up new page into free pool blocks\r
289 //\r
162ed594 290 Offset = 0;\r
291 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
28a00297 292 ASSERT (Index < MAX_POOL_LIST);\r
293 FSize = LIST_TO_SIZE(Index);\r
294\r
162ed594 295 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
296 Free = (POOL_FREE *) &NewPage[Offset]; \r
28a00297 297 Free->Signature = POOL_FREE_SIGNATURE;\r
298 Free->Index = (UINT32)Index;\r
299 InsertHeadList (&Pool->FreeList[Index], &Free->Link);\r
162ed594 300 Offset += FSize;\r
28a00297 301 }\r
302\r
303 Index -= 1;\r
304 }\r
305\r
162ed594 306 ASSERT (Offset == DEFAULT_PAGE_ALLOCATION);\r
28a00297 307 Index = SIZE_TO_LIST(Size);\r
308 }\r
309\r
310 //\r
311 // Remove entry from free pool list\r
312 //\r
313 Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);\r
314 RemoveEntryList (&Free->Link);\r
315\r
316 Head = (POOL_HEAD *) Free;\r
317\r
318Done:\r
319 Buffer = NULL;\r
320\r
321 if (Head != NULL) {\r
322 \r
323 //\r
324 // If we have a pool buffer, fill in the header & tail info\r
325 //\r
326 Head->Signature = POOL_HEAD_SIGNATURE;\r
327 Head->Size = (UINT32) Size;\r
328 Head->Type = (EFI_MEMORY_TYPE) PoolType;\r
329 Tail = HEAD_TO_TAIL (Head);\r
330 Tail->Signature = POOL_TAIL_SIGNATURE;\r
331 Tail->Size = (UINT32) Size;\r
332 Buffer = Head->Data;\r
333 DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);\r
334\r
e94a9ff7 335 DEBUG ((\r
336 DEBUG_POOL,\r
337 "AllocatePoolI: Type %x, Addr %x (len %x) %,d\n", PoolType, \r
338 Buffer, \r
339 Size - POOL_OVERHEAD, \r
340 Pool->Used\r
341 ));\r
28a00297 342\r
343 //\r
344 // Account the allocation\r
345 //\r
346 Pool->Used += Size;\r
347\r
348 } else {\r
162ed594 349 DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %d bytes\n", Size));\r
28a00297 350 }\r
351\r
352 return Buffer;\r
353}\r
354 \r
355\r
356\r
162ed594 357/**\r
28a00297 358 Frees pool.\r
359\r
162ed594 360 @param Buffer The allocated pool entry to free \r
28a00297 361\r
162ed594 362 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. \r
363 @retval EFI_SUCCESS Pool successfully freed.\r
28a00297 364\r
162ed594 365**/\r
366EFI_STATUS\r
367EFIAPI\r
368CoreFreePool (\r
369 IN VOID *Buffer\r
370 )\r
28a00297 371{\r
372 EFI_STATUS Status;\r
373\r
e94a9ff7 374 if (Buffer == NULL) {\r
28a00297 375 return EFI_INVALID_PARAMETER;\r
376 }\r
377\r
378 CoreAcquireMemoryLock ();\r
379 Status = CoreFreePoolI (Buffer);\r
380 CoreReleaseMemoryLock ();\r
381 return Status;\r
382}\r
383\r
384\r
28a00297 385\r
162ed594 386/**\r
28a00297 387 Internal function to free a pool entry.\r
28a00297 388 Caller must have the memory lock held\r
389\r
162ed594 390 @param Buffer The allocated pool entry to free \r
28a00297 391\r
162ed594 392 @retval EFI_INVALID_PARAMETER Buffer not valid \r
393 @retval EFI_SUCCESS Buffer successfully freed.\r
28a00297 394\r
162ed594 395**/\r
396EFI_STATUS\r
397CoreFreePoolI (\r
398 IN VOID *Buffer\r
399 )\r
28a00297 400{\r
401 POOL *Pool;\r
402 POOL_HEAD *Head;\r
403 POOL_TAIL *Tail;\r
404 POOL_FREE *Free;\r
405 UINTN Index;\r
406 UINTN NoPages;\r
407 UINTN Size;\r
408 CHAR8 *NewPage;\r
409 UINTN FSize;\r
162ed594 410 UINTN Offset;\r
28a00297 411 BOOLEAN AllFree;\r
412\r
413 ASSERT(NULL != Buffer);\r
414 //\r
415 // Get the head & tail of the pool entry\r
416 //\r
417 Head = CR (Buffer, POOL_HEAD, Data, POOL_HEAD_SIGNATURE);\r
418 ASSERT(NULL != Head);\r
419\r
420 if (Head->Signature != POOL_HEAD_SIGNATURE) {\r
421 return EFI_INVALID_PARAMETER;\r
422 }\r
423\r
424 Tail = HEAD_TO_TAIL (Head);\r
425 ASSERT(NULL != Tail);\r
426\r
427 //\r
428 // Debug\r
429 //\r
430 ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);\r
431 ASSERT (Head->Size == Tail->Size);\r
432 ASSERT_LOCKED (&gMemoryLock);\r
433\r
434 if (Tail->Signature != POOL_TAIL_SIGNATURE) {\r
435 return EFI_INVALID_PARAMETER;\r
436 }\r
437\r
438 if (Head->Size != Tail->Size) {\r
439 return EFI_INVALID_PARAMETER;\r
440 }\r
441\r
442 //\r
443 // Determine the pool type and account for it\r
444 //\r
445 Size = Head->Size;\r
446 Pool = LookupPoolHead (Head->Type);\r
447 if (Pool == NULL) {\r
448 return EFI_INVALID_PARAMETER;\r
449 }\r
450 Pool->Used -= Size;\r
162ed594 451 DEBUG ((DEBUG_POOL, "FreePool: %x (len %x) %,d\n", Head->Data, Head->Size - POOL_OVERHEAD, Pool->Used));\r
28a00297 452\r
453 //\r
454 // Determine the pool list \r
455 //\r
456 Index = SIZE_TO_LIST(Size);\r
457 DEBUG_CLEAR_MEMORY (Head, Size);\r
458\r
459 //\r
460 // If it's not on the list, it must be pool pages\r
461 //\r
462 if (Index >= MAX_POOL_LIST) {\r
463\r
464 //\r
465 // Return the memory pages back to free memory\r
466 //\r
467 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1;\r
468 NoPages &= ~(EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1);\r
469 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);\r
470\r
471 } else {\r
472\r
473 //\r
474 // Put the pool entry onto the free pool list\r
475 //\r
476 Free = (POOL_FREE *) Head;\r
477 ASSERT(NULL != Free);\r
478 Free->Signature = POOL_FREE_SIGNATURE;\r
479 Free->Index = (UINT32)Index;\r
480 InsertHeadList (&Pool->FreeList[Index], &Free->Link);\r
481\r
482 //\r
483 // See if all the pool entries in the same page as Free are freed pool \r
484 // entries\r
485 //\r
486 NewPage = (CHAR8 *)((UINTN)Free & ~((DEFAULT_PAGE_ALLOCATION) -1));\r
487 Free = (POOL_FREE *) &NewPage[0];\r
e94a9ff7 488 ASSERT(Free != NULL);\r
28a00297 489\r
490 if (Free->Signature == POOL_FREE_SIGNATURE) {\r
491\r
492 Index = Free->Index;\r
493\r
494 AllFree = TRUE;\r
162ed594 495 Offset = 0;\r
28a00297 496 \r
162ed594 497 while ((Offset < DEFAULT_PAGE_ALLOCATION) && (AllFree)) {\r
28a00297 498 FSize = LIST_TO_SIZE(Index);\r
162ed594 499 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
500 Free = (POOL_FREE *) &NewPage[Offset];\r
28a00297 501 ASSERT(NULL != Free);\r
502 if (Free->Signature != POOL_FREE_SIGNATURE) {\r
503 AllFree = FALSE;\r
504 }\r
162ed594 505 Offset += FSize;\r
28a00297 506 }\r
507 Index -= 1;\r
508 }\r
509\r
510 if (AllFree) {\r
511\r
512 //\r
513 // All of the pool entries in the same page as Free are free pool \r
514 // entries\r
515 // Remove all of these pool entries from the free loop lists.\r
516 //\r
517 Free = (POOL_FREE *) &NewPage[0];\r
518 ASSERT(NULL != Free);\r
519 Index = Free->Index;\r
162ed594 520 Offset = 0;\r
28a00297 521 \r
162ed594 522 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
28a00297 523 FSize = LIST_TO_SIZE(Index);\r
162ed594 524 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
525 Free = (POOL_FREE *) &NewPage[Offset];\r
28a00297 526 ASSERT(NULL != Free);\r
527 RemoveEntryList (&Free->Link);\r
162ed594 528 Offset += FSize;\r
28a00297 529 }\r
530 Index -= 1;\r
531 }\r
532\r
533 //\r
534 // Free the page\r
535 //\r
536 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN)NewPage, EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION));\r
537 }\r
538 }\r
539 }\r
540\r
541 //\r
542 // If this is an OS specific memory type, then check to see if the last \r
543 // portion of that memory type has been freed. If it has, then free the\r
544 // list entry for that memory type\r
545 //\r
546 if (Pool->MemoryType < 0 && Pool->Used == 0) {\r
547 RemoveEntryList (&Pool->Link);\r
548 CoreFreePoolI (Pool);\r
549 }\r
550\r
551 return EFI_SUCCESS;\r
552}\r