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