]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Core/Dxe/Mem/Pool.c
Enhance the error handling for AllocatePool and AllocatePages function.
[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 - 2012, 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 or Buffer is NULL. \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 if (Buffer == NULL) {\r
194 return EFI_INVALID_PARAMETER;\r
195 }\r
196\r
197 *Buffer = NULL;\r
198\r
199 //\r
200 // If size is too large, fail it\r
201 // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES\r
202 //\r
203 if (Size > MAX_POOL_SIZE) {\r
204 return EFI_OUT_OF_RESOURCES;\r
205 }\r
206\r
207 //\r
208 // Acquire the memory lock and make the allocation\r
209 //\r
210 Status = CoreAcquireLockOrFail (&gMemoryLock);\r
211 if (EFI_ERROR (Status)) {\r
212 return EFI_OUT_OF_RESOURCES;\r
213 }\r
214\r
215 *Buffer = CoreAllocatePoolI (PoolType, Size);\r
216 CoreReleaseMemoryLock ();\r
217 return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;\r
218}\r
219\r
220\r
221\r
222/**\r
223 Internal function to allocate pool of a particular type.\r
224 Caller must have the memory lock held\r
225\r
226 @param PoolType Type of pool to allocate\r
227 @param Size The amount of pool to allocate\r
228\r
229 @return The allocate pool, or NULL\r
230\r
231**/\r
232VOID *\r
233CoreAllocatePoolI (\r
234 IN EFI_MEMORY_TYPE PoolType,\r
235 IN UINTN Size\r
236 )\r
237{\r
238 POOL *Pool;\r
239 POOL_FREE *Free;\r
240 POOL_HEAD *Head;\r
241 POOL_TAIL *Tail;\r
242 CHAR8 *NewPage;\r
243 VOID *Buffer;\r
244 UINTN Index;\r
245 UINTN FSize;\r
246 UINTN Offset;\r
247 UINTN NoPages;\r
248\r
249 ASSERT_LOCKED (&gMemoryLock);\r
250\r
251 //\r
252 // Adjust the size by the pool header & tail overhead\r
253 //\r
254\r
255 //\r
256 // Adjusting the Size to be of proper alignment so that\r
257 // we don't get an unaligned access fault later when\r
258 // pool_Tail is being initialized\r
259 //\r
260 Size = ALIGN_VARIABLE (Size);\r
261\r
262 Size += POOL_OVERHEAD;\r
263 Index = SIZE_TO_LIST(Size);\r
264 Pool = LookupPoolHead (PoolType);\r
265 if (Pool== NULL) {\r
266 return NULL;\r
267 }\r
268 Head = NULL;\r
269\r
270 //\r
271 // If allocation is over max size, just allocate pages for the request\r
272 // (slow)\r
273 //\r
274 if (Index >= MAX_POOL_LIST) {\r
275 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1;\r
276 NoPages &= ~(EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1);\r
277 Head = CoreAllocatePoolPages (PoolType, NoPages, DEFAULT_PAGE_ALLOCATION);\r
278 goto Done;\r
279 }\r
280\r
281 //\r
282 // If there's no free pool in the proper list size, go get some more pages\r
283 //\r
284 if (IsListEmpty (&Pool->FreeList[Index])) {\r
285\r
286 //\r
287 // Get another page\r
288 //\r
289 NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION), DEFAULT_PAGE_ALLOCATION);\r
290 if (NewPage == NULL) {\r
291 goto Done;\r
292 }\r
293\r
294 //\r
295 // Carve up new page into free pool blocks\r
296 //\r
297 Offset = 0;\r
298 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
299 ASSERT (Index < MAX_POOL_LIST);\r
300 FSize = LIST_TO_SIZE(Index);\r
301\r
302 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
303 Free = (POOL_FREE *) &NewPage[Offset];\r
304 Free->Signature = POOL_FREE_SIGNATURE;\r
305 Free->Index = (UINT32)Index;\r
306 InsertHeadList (&Pool->FreeList[Index], &Free->Link);\r
307 Offset += FSize;\r
308 }\r
309\r
310 Index -= 1;\r
311 }\r
312\r
313 ASSERT (Offset == DEFAULT_PAGE_ALLOCATION);\r
314 Index = SIZE_TO_LIST(Size);\r
315 }\r
316\r
317 //\r
318 // Remove entry from free pool list\r
319 //\r
320 Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE);\r
321 RemoveEntryList (&Free->Link);\r
322\r
323 Head = (POOL_HEAD *) Free;\r
324\r
325Done:\r
326 Buffer = NULL;\r
327\r
328 if (Head != NULL) {\r
329\r
330 //\r
331 // If we have a pool buffer, fill in the header & tail info\r
332 //\r
333 Head->Signature = POOL_HEAD_SIGNATURE;\r
334 Head->Size = (UINT32) Size;\r
335 Head->Type = (EFI_MEMORY_TYPE) PoolType;\r
336 Tail = HEAD_TO_TAIL (Head);\r
337 Tail->Signature = POOL_TAIL_SIGNATURE;\r
338 Tail->Size = (UINT32) Size;\r
339 Buffer = Head->Data;\r
340 DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);\r
341\r
342 DEBUG ((\r
343 DEBUG_POOL,\r
344 "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,\r
345 Buffer,\r
346 (UINT64)(Size - POOL_OVERHEAD),\r
347 (UINT64) Pool->Used\r
348 ));\r
349\r
350 //\r
351 // Account the allocation\r
352 //\r
353 Pool->Used += Size;\r
354\r
355 } else {\r
356 DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));\r
357 }\r
358\r
359 return Buffer;\r
360}\r
361\r
362\r
363\r
364/**\r
365 Frees pool.\r
366\r
367 @param Buffer The allocated pool entry to free\r
368\r
369 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.\r
370 @retval EFI_SUCCESS Pool successfully freed.\r
371\r
372**/\r
373EFI_STATUS\r
374EFIAPI\r
375CoreFreePool (\r
376 IN VOID *Buffer\r
377 )\r
378{\r
379 EFI_STATUS Status;\r
380\r
381 if (Buffer == NULL) {\r
382 return EFI_INVALID_PARAMETER;\r
383 }\r
384\r
385 CoreAcquireMemoryLock ();\r
386 Status = CoreFreePoolI (Buffer);\r
387 CoreReleaseMemoryLock ();\r
388 return Status;\r
389}\r
390\r
391\r
392\r
393/**\r
394 Internal function to free a pool entry.\r
395 Caller must have the memory lock held\r
396\r
397 @param Buffer The allocated pool entry to free\r
398\r
399 @retval EFI_INVALID_PARAMETER Buffer not valid\r
400 @retval EFI_SUCCESS Buffer successfully freed.\r
401\r
402**/\r
403EFI_STATUS\r
404CoreFreePoolI (\r
405 IN VOID *Buffer\r
406 )\r
407{\r
408 POOL *Pool;\r
409 POOL_HEAD *Head;\r
410 POOL_TAIL *Tail;\r
411 POOL_FREE *Free;\r
412 UINTN Index;\r
413 UINTN NoPages;\r
414 UINTN Size;\r
415 CHAR8 *NewPage;\r
416 UINTN FSize;\r
417 UINTN Offset;\r
418 BOOLEAN AllFree;\r
419\r
420 ASSERT(Buffer != NULL);\r
421 //\r
422 // Get the head & tail of the pool entry\r
423 //\r
424 Head = CR (Buffer, POOL_HEAD, Data, POOL_HEAD_SIGNATURE);\r
425 ASSERT(Head != NULL);\r
426\r
427 if (Head->Signature != POOL_HEAD_SIGNATURE) {\r
428 return EFI_INVALID_PARAMETER;\r
429 }\r
430\r
431 Tail = HEAD_TO_TAIL (Head);\r
432 ASSERT(Tail != NULL);\r
433\r
434 //\r
435 // Debug\r
436 //\r
437 ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);\r
438 ASSERT (Head->Size == Tail->Size);\r
439 ASSERT_LOCKED (&gMemoryLock);\r
440\r
441 if (Tail->Signature != POOL_TAIL_SIGNATURE) {\r
442 return EFI_INVALID_PARAMETER;\r
443 }\r
444\r
445 if (Head->Size != Tail->Size) {\r
446 return EFI_INVALID_PARAMETER;\r
447 }\r
448\r
449 //\r
450 // Determine the pool type and account for it\r
451 //\r
452 Size = Head->Size;\r
453 Pool = LookupPoolHead (Head->Type);\r
454 if (Pool == NULL) {\r
455 return EFI_INVALID_PARAMETER;\r
456 }\r
457 Pool->Used -= Size;\r
458 DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used));\r
459\r
460 //\r
461 // Determine the pool list\r
462 //\r
463 Index = SIZE_TO_LIST(Size);\r
464 DEBUG_CLEAR_MEMORY (Head, Size);\r
465\r
466 //\r
467 // If it's not on the list, it must be pool pages\r
468 //\r
469 if (Index >= MAX_POOL_LIST) {\r
470\r
471 //\r
472 // Return the memory pages back to free memory\r
473 //\r
474 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1;\r
475 NoPages &= ~(EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION) - 1);\r
476 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);\r
477\r
478 } else {\r
479\r
480 //\r
481 // Put the pool entry onto the free pool list\r
482 //\r
483 Free = (POOL_FREE *) Head;\r
484 ASSERT(Free != NULL);\r
485 Free->Signature = POOL_FREE_SIGNATURE;\r
486 Free->Index = (UINT32)Index;\r
487 InsertHeadList (&Pool->FreeList[Index], &Free->Link);\r
488\r
489 //\r
490 // See if all the pool entries in the same page as Free are freed pool\r
491 // entries\r
492 //\r
493 NewPage = (CHAR8 *)((UINTN)Free & ~((DEFAULT_PAGE_ALLOCATION) -1));\r
494 Free = (POOL_FREE *) &NewPage[0];\r
495 ASSERT(Free != NULL);\r
496\r
497 if (Free->Signature == POOL_FREE_SIGNATURE) {\r
498\r
499 Index = Free->Index;\r
500\r
501 AllFree = TRUE;\r
502 Offset = 0;\r
503\r
504 while ((Offset < DEFAULT_PAGE_ALLOCATION) && (AllFree)) {\r
505 FSize = LIST_TO_SIZE(Index);\r
506 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
507 Free = (POOL_FREE *) &NewPage[Offset];\r
508 ASSERT(Free != NULL);\r
509 if (Free->Signature != POOL_FREE_SIGNATURE) {\r
510 AllFree = FALSE;\r
511 }\r
512 Offset += FSize;\r
513 }\r
514 Index -= 1;\r
515 }\r
516\r
517 if (AllFree) {\r
518\r
519 //\r
520 // All of the pool entries in the same page as Free are free pool\r
521 // entries\r
522 // Remove all of these pool entries from the free loop lists.\r
523 //\r
524 Free = (POOL_FREE *) &NewPage[0];\r
525 ASSERT(Free != NULL);\r
526 Index = Free->Index;\r
527 Offset = 0;\r
528\r
529 while (Offset < DEFAULT_PAGE_ALLOCATION) {\r
530 FSize = LIST_TO_SIZE(Index);\r
531 while (Offset + FSize <= DEFAULT_PAGE_ALLOCATION) {\r
532 Free = (POOL_FREE *) &NewPage[Offset];\r
533 ASSERT(Free != NULL);\r
534 RemoveEntryList (&Free->Link);\r
535 Offset += FSize;\r
536 }\r
537 Index -= 1;\r
538 }\r
539\r
540 //\r
541 // Free the page\r
542 //\r
543 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN)NewPage, EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION));\r
544 }\r
545 }\r
546 }\r
547\r
548 //\r
549 // If this is an OS specific memory type, then check to see if the last\r
550 // portion of that memory type has been freed. If it has, then free the\r
551 // list entry for that memory type\r
552 //\r
553 if (Pool->MemoryType < 0 && Pool->Used == 0) {\r
554 RemoveEntryList (&Pool->Link);\r
555 CoreFreePoolI (Pool);\r
556 }\r
557\r
558 return EFI_SUCCESS;\r
559}\r