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