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