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