]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Core/Dxe/Mem/Pool.c
Clean up DEC files:
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Mem / Pool.c
... / ...
CommitLineData
1/** @file\r
2 UEFI Memory pool management functions.\r
3\r
4Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
5This 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
12\r
13**/\r
14\r
15#include "DxeMain.h"\r
16#include "Imem.h"\r
17\r
18#define POOL_FREE_SIGNATURE 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 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 OFFSET_OF(POOL_HEAD,Data)\r
36\r
37#define POOL_TAIL_SIGNATURE 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 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
73// Pool header for each memory type.\r
74//\r
75POOL mPoolHead[EfiMaxMemoryType];\r
76\r
77//\r
78// List of pool header to search for the appropriate memory type.\r
79//\r
80LIST_ENTRY mPoolHeadList = INITIALIZE_LIST_HEAD_VARIABLE (mPoolHeadList);\r
81\r
82\r
83/**\r
84 Called to initialize the pool.\r
85\r
86**/\r
87VOID\r
88CoreInitializePool (\r
89 VOID\r
90 )\r
91{\r
92 UINTN Type;\r
93 UINTN Index;\r
94\r
95 for (Type=0; Type < EfiMaxMemoryType; Type++) {\r
96 mPoolHead[Type].Signature = 0;\r
97 mPoolHead[Type].Used = 0;\r
98 mPoolHead[Type].MemoryType = (EFI_MEMORY_TYPE) Type;\r
99 for (Index=0; Index < MAX_POOL_LIST; Index++) {\r
100 InitializeListHead (&mPoolHead[Type].FreeList[Index]);\r
101 }\r
102 }\r
103}\r
104\r
105\r
106/**\r
107 Look up pool head for specified memory type.\r
108\r
109 @param MemoryType Memory type of which pool head is looked for\r
110\r
111 @return Pointer of Corresponding pool head.\r
112\r
113**/\r
114POOL *\r
115LookupPoolHead (\r
116 IN EFI_MEMORY_TYPE MemoryType\r
117 )\r
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 &mPoolHead[MemoryType];\r
125 }\r
126\r
127 //\r
128 // MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI \r
129 // OS loaders that are provided by operating system vendors\r
130 //\r
131 if (MemoryType >= (INT32)0x80000000 && MemoryType <= (INT32)0xffffffff) {\r
132\r
133 for (Link = mPoolHeadList.ForwardLink; Link != &mPoolHeadList; Link = Link->ForwardLink) {\r
134 Pool = CR(Link, POOL, Link, POOL_SIGNATURE);\r
135 if (Pool->MemoryType == MemoryType) {\r
136 return Pool;\r
137 }\r
138 }\r
139\r
140 Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL));\r
141 if (Pool == NULL) {\r
142 return NULL;\r
143 }\r
144\r
145 Pool->Signature = POOL_SIGNATURE;\r
146 Pool->Used = 0;\r
147 Pool->MemoryType = MemoryType;\r
148 for (Index=0; Index < MAX_POOL_LIST; Index++) {\r
149 InitializeListHead (&Pool->FreeList[Index]);\r
150 }\r
151\r
152 InsertHeadList (&mPoolHeadList, &Pool->Link);\r
153\r
154 return Pool;\r
155 }\r
156\r
157 return NULL;\r
158}\r
159\r
160\r
161\r
162/**\r
163 Allocate pool of a particular type.\r
164\r
165 @param PoolType Type of pool to allocate\r
166 @param Size The amount of pool to allocate\r
167 @param Buffer The address to return a pointer to the allocated\r
168 pool\r
169\r
170 @retval EFI_INVALID_PARAMETER PoolType not valid\r
171 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.\r
172 @retval EFI_SUCCESS Pool successfully allocated.\r
173\r
174**/\r
175EFI_STATUS\r
176EFIAPI\r
177CoreAllocatePool (\r
178 IN EFI_MEMORY_TYPE PoolType,\r
179 IN UINTN Size,\r
180 OUT VOID **Buffer\r
181 )\r
182{\r
183 EFI_STATUS Status;\r
184\r
185 //\r
186 // If it's not a valid type, fail it\r
187 //\r
188 if ((PoolType >= EfiMaxMemoryType && PoolType <= 0x7fffffff) ||\r
189 PoolType == EfiConventionalMemory) {\r
190 return EFI_INVALID_PARAMETER;\r
191 }\r
192\r
193 *Buffer = NULL;\r
194\r
195 //\r
196 // If size is too large, fail it\r
197 // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES\r
198 //\r
199 if (Size > MAX_POOL_SIZE) {\r
200 return EFI_OUT_OF_RESOURCES;\r
201 }\r
202\r
203 //\r
204 // Acquire the memory lock and make the allocation\r
205 //\r
206 Status = CoreAcquireLockOrFail (&gMemoryLock);\r
207 if (EFI_ERROR (Status)) {\r
208 return EFI_OUT_OF_RESOURCES;\r
209 }\r
210\r
211 *Buffer = CoreAllocatePoolI (PoolType, Size);\r
212 CoreReleaseMemoryLock ();\r
213 return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;\r
214}\r
215\r
216\r
217\r
218/**\r
219 Internal function to allocate pool of a particular type.\r
220 Caller must have the memory lock held\r
221\r
222 @param PoolType Type of pool to allocate\r
223 @param Size The amount of pool to allocate\r
224\r
225 @return The allocate pool, or NULL\r
226\r
227**/\r
228VOID *\r
229CoreAllocatePoolI (\r
230 IN EFI_MEMORY_TYPE PoolType,\r
231 IN UINTN Size\r
232 )\r
233{\r
234 POOL *Pool;\r
235 POOL_FREE *Free;\r
236 POOL_HEAD *Head;\r
237 POOL_TAIL *Tail;\r
238 CHAR8 *NewPage;\r
239 VOID *Buffer;\r
240 UINTN Index;\r
241 UINTN FSize;\r
242 UINTN Offset;\r
243 UINTN NoPages;\r
244\r
245 ASSERT_LOCKED (&gMemoryLock);\r
246\r
247 //\r
248 // Adjust the size by the pool header & tail overhead\r
249 //\r
250\r
251 //\r
252 // Adjusting the Size to be of proper alignment so that\r
253 // we don't get an unaligned access fault later when\r
254 // pool_Tail is being initialized\r
255 //\r
256 Size = ALIGN_VARIABLE (Size);\r
257\r
258 Size += POOL_OVERHEAD;\r
259 Index = SIZE_TO_LIST(Size);\r
260 Pool = LookupPoolHead (PoolType);\r
261 if (Pool== NULL) {\r
262 return NULL;\r
263 }\r
264 Head = NULL;\r
265\r
266 //\r
267 // If allocation is over max size, just allocate pages for the request\r
268 // (slow)\r
269 //\r
270 if (Index >= MAX_POOL_LIST) {\r
271 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1;\r
272 NoPages &= ~(EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1);\r
273 Head = CoreAllocatePoolPages (PoolType, NoPages, DEFAULT_PAGE_ALLOCATION);\r
274 goto Done;\r
275 }\r
276\r
277 //\r
278 // If there's no free pool in the proper list size, go get some more pages\r
279 //\r
280 if (IsListEmpty (&Pool->FreeList[Index])) {\r
281\r
282 //\r
283 // Get another page\r
284 //\r
285 NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION), DEFAULT_PAGE_ALLOCATION);\r
286 if (NewPage == NULL) {\r
287 goto Done;\r
288 }\r
289\r
290 //\r
291 // Carve up new page into free pool blocks\r
292 //\r
293 Offset = 0;\r
294 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
295 ASSERT (Index < MAX_POOL_LIST);\r
296 FSize = LIST_TO_SIZE(Index);\r
297\r
298 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
299 Free = (POOL_FREE *) &NewPage[Offset];\r
300 Free->Signature = POOL_FREE_SIGNATURE;\r
301 Free->Index = (UINT32)Index;\r
302 InsertHeadList (&Pool->FreeList[Index], &Free->Link);\r
303 Offset += FSize;\r
304 }\r
305\r
306 Index -= 1;\r
307 }\r
308\r
309 ASSERT (Offset == DEFAULT_PAGE_ALLOCATION);\r
310 Index = SIZE_TO_LIST(Size);\r
311 }\r
312\r
313 //\r
314 // Remove entry from free pool list\r
315 //\r
316 Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);\r
317 RemoveEntryList (&Free->Link);\r
318\r
319 Head = (POOL_HEAD *) Free;\r
320\r
321Done:\r
322 Buffer = NULL;\r
323\r
324 if (Head != NULL) {\r
325\r
326 //\r
327 // If we have a pool buffer, fill in the header & tail info\r
328 //\r
329 Head->Signature = POOL_HEAD_SIGNATURE;\r
330 Head->Size = (UINT32) Size;\r
331 Head->Type = (EFI_MEMORY_TYPE) PoolType;\r
332 Tail = HEAD_TO_TAIL (Head);\r
333 Tail->Signature = POOL_TAIL_SIGNATURE;\r
334 Tail->Size = (UINT32) Size;\r
335 Buffer = Head->Data;\r
336 DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);\r
337\r
338 DEBUG ((\r
339 DEBUG_POOL,\r
340 "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,\r
341 Buffer,\r
342 (UINT64)(Size - POOL_OVERHEAD),\r
343 (UINT64) Pool->Used\r
344 ));\r
345\r
346 //\r
347 // Account the allocation\r
348 //\r
349 Pool->Used += Size;\r
350\r
351 } else {\r
352 DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));\r
353 }\r
354\r
355 return Buffer;\r
356}\r
357\r
358\r
359\r
360/**\r
361 Frees pool.\r
362\r
363 @param Buffer The allocated pool entry to free\r
364\r
365 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.\r
366 @retval EFI_SUCCESS Pool successfully freed.\r
367\r
368**/\r
369EFI_STATUS\r
370EFIAPI\r
371CoreFreePool (\r
372 IN VOID *Buffer\r
373 )\r
374{\r
375 EFI_STATUS Status;\r
376\r
377 if (Buffer == NULL) {\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
388\r
389/**\r
390 Internal function to free a pool entry.\r
391 Caller must have the memory lock held\r
392\r
393 @param Buffer The allocated pool entry to free\r
394\r
395 @retval EFI_INVALID_PARAMETER Buffer not valid\r
396 @retval EFI_SUCCESS Buffer successfully freed.\r
397\r
398**/\r
399EFI_STATUS\r
400CoreFreePoolI (\r
401 IN VOID *Buffer\r
402 )\r
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
413 UINTN Offset;\r
414 BOOLEAN AllFree;\r
415\r
416 ASSERT(Buffer != NULL);\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(Head != NULL);\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(Tail != NULL);\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
454 DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));\r
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(Free != NULL);\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(Free != NULL);\r
492\r
493 if (Free->Signature == POOL_FREE_SIGNATURE) {\r
494\r
495 Index = Free->Index;\r
496\r
497 AllFree = TRUE;\r
498 Offset = 0;\r
499\r
500 while ((Offset < DEFAULT_PAGE_ALLOCATION) && (AllFree)) {\r
501 FSize = LIST_TO_SIZE(Index);\r
502 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
503 Free = (POOL_FREE *) &NewPage[Offset];\r
504 ASSERT(Free != NULL);\r
505 if (Free->Signature != POOL_FREE_SIGNATURE) {\r
506 AllFree = FALSE;\r
507 }\r
508 Offset += FSize;\r
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(Free != NULL);\r
522 Index = Free->Index;\r
523 Offset = 0;\r
524\r
525 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
526 FSize = LIST_TO_SIZE(Index);\r
527 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
528 Free = (POOL_FREE *) &NewPage[Offset];\r
529 ASSERT(Free != NULL);\r
530 RemoveEntryList (&Free->Link);\r
531 Offset += FSize;\r
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