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