1 /** @file 2 UEFI Memory pool management functions. 3 4 Copyright (c) 2006 - 2016, 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 128, 256, 384, 640, 1024, 1664, 2688, 4352, 7040, 11392, 18432, 29824 56 }; 57 58 #define SIZE_TO_LIST(a) (GetPoolIndexFromSize (a)) 59 #define LIST_TO_SIZE(a) (mPoolSizeTable [a]) 60 61 #define MAX_POOL_LIST (ARRAY_SIZE (mPoolSizeTable)) 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 Buffer is NULL. 201 PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. 202 PoolType is EfiPersistentMemory. 203 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 204 @retval EFI_SUCCESS Pool successfully allocated. 205 206 **/ 207 EFI_STATUS 208 EFIAPI 209 CoreInternalAllocatePool ( 210 IN EFI_MEMORY_TYPE PoolType, 211 IN UINTN Size, 212 OUT VOID **Buffer 213 ) 214 { 215 EFI_STATUS Status; 216 217 // 218 // If it's not a valid type, fail it 219 // 220 if ((PoolType >= EfiMaxMemoryType && PoolType < MEMORY_TYPE_OEM_RESERVED_MIN) || 221 (PoolType == EfiConventionalMemory) || (PoolType == EfiPersistentMemory)) { 222 return EFI_INVALID_PARAMETER; 223 } 224 225 if (Buffer == NULL) { 226 return EFI_INVALID_PARAMETER; 227 } 228 229 *Buffer = NULL; 230 231 // 232 // If size is too large, fail it 233 // Base on the EFI spec, return status of EFI_OUT_OF_RESOURCES 234 // 235 if (Size > MAX_POOL_SIZE) { 236 return EFI_OUT_OF_RESOURCES; 237 } 238 239 // 240 // Acquire the memory lock and make the allocation 241 // 242 Status = CoreAcquireLockOrFail (&gMemoryLock); 243 if (EFI_ERROR (Status)) { 244 return EFI_OUT_OF_RESOURCES; 245 } 246 247 *Buffer = CoreAllocatePoolI (PoolType, Size); 248 CoreReleaseMemoryLock (); 249 return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; 250 } 251 252 /** 253 Allocate pool of a particular type. 254 255 @param PoolType Type of pool to allocate 256 @param Size The amount of pool to allocate 257 @param Buffer The address to return a pointer to the allocated 258 pool 259 260 @retval EFI_INVALID_PARAMETER Buffer is NULL. 261 PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. 262 PoolType is EfiPersistentMemory. 263 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 264 @retval EFI_SUCCESS Pool successfully allocated. 265 266 **/ 267 EFI_STATUS 268 EFIAPI 269 CoreAllocatePool ( 270 IN EFI_MEMORY_TYPE PoolType, 271 IN UINTN Size, 272 OUT VOID **Buffer 273 ) 274 { 275 EFI_STATUS Status; 276 277 Status = CoreInternalAllocatePool (PoolType, Size, Buffer); 278 if (!EFI_ERROR (Status)) { 279 CoreUpdateProfile ( 280 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 281 MemoryProfileActionAllocatePool, 282 PoolType, 283 Size, 284 *Buffer, 285 NULL 286 ); 287 InstallMemoryAttributesTableOnMemoryAllocation (PoolType); 288 } 289 return Status; 290 } 291 292 /** 293 Internal function to allocate pool of a particular type. 294 Caller must have the memory lock held 295 296 @param PoolType Type of pool to allocate 297 @param Size The amount of pool to allocate 298 299 @return The allocate pool, or NULL 300 301 **/ 302 VOID * 303 CoreAllocatePoolI ( 304 IN EFI_MEMORY_TYPE PoolType, 305 IN UINTN Size 306 ) 307 { 308 POOL *Pool; 309 POOL_FREE *Free; 310 POOL_HEAD *Head; 311 POOL_TAIL *Tail; 312 CHAR8 *NewPage; 313 VOID *Buffer; 314 UINTN Index; 315 UINTN FSize; 316 UINTN Offset, MaxOffset; 317 UINTN NoPages; 318 UINTN Granularity; 319 320 ASSERT_LOCKED (&gMemoryLock); 321 322 if (PoolType == EfiACPIReclaimMemory || 323 PoolType == EfiACPIMemoryNVS || 324 PoolType == EfiRuntimeServicesCode || 325 PoolType == EfiRuntimeServicesData) { 326 327 Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT; 328 } else { 329 Granularity = DEFAULT_PAGE_ALLOCATION; 330 } 331 332 // 333 // Adjust the size by the pool header & tail overhead 334 // 335 336 // 337 // Adjusting the Size to be of proper alignment so that 338 // we don't get an unaligned access fault later when 339 // pool_Tail is being initialized 340 // 341 Size = ALIGN_VARIABLE (Size); 342 343 Size += POOL_OVERHEAD; 344 Index = SIZE_TO_LIST(Size); 345 Pool = LookupPoolHead (PoolType); 346 if (Pool== NULL) { 347 return NULL; 348 } 349 Head = NULL; 350 351 // 352 // If allocation is over max size, just allocate pages for the request 353 // (slow) 354 // 355 if (Index >= SIZE_TO_LIST (Granularity)) { 356 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1; 357 NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1); 358 Head = CoreAllocatePoolPages (PoolType, NoPages, Granularity); 359 goto Done; 360 } 361 362 // 363 // If there's no free pool in the proper list size, go get some more pages 364 // 365 if (IsListEmpty (&Pool->FreeList[Index])) { 366 367 Offset = LIST_TO_SIZE (Index); 368 MaxOffset = Granularity; 369 370 // 371 // Check the bins holding larger blocks, and carve one up if needed 372 // 373 while (++Index < SIZE_TO_LIST (Granularity)) { 374 if (!IsListEmpty (&Pool->FreeList[Index])) { 375 Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE); 376 RemoveEntryList (&Free->Link); 377 NewPage = (VOID *) Free; 378 MaxOffset = LIST_TO_SIZE (Index); 379 goto Carve; 380 } 381 } 382 383 // 384 // Get another page 385 // 386 NewPage = CoreAllocatePoolPages(PoolType, EFI_SIZE_TO_PAGES (Granularity), Granularity); 387 if (NewPage == NULL) { 388 goto Done; 389 } 390 391 // 392 // Serve the allocation request from the head of the allocated block 393 // 394 Carve: 395 Head = (POOL_HEAD *) NewPage; 396 397 // 398 // Carve up remaining space into free pool blocks 399 // 400 Index--; 401 while (Offset < MaxOffset) { 402 ASSERT (Index < MAX_POOL_LIST); 403 FSize = LIST_TO_SIZE(Index); 404 405 while (Offset + FSize <= MaxOffset) { 406 Free = (POOL_FREE *) &NewPage[Offset]; 407 Free->Signature = POOL_FREE_SIGNATURE; 408 Free->Index = (UINT32)Index; 409 InsertHeadList (&Pool->FreeList[Index], &Free->Link); 410 Offset += FSize; 411 } 412 Index -= 1; 413 } 414 415 ASSERT (Offset == MaxOffset); 416 goto Done; 417 } 418 419 // 420 // Remove entry from free pool list 421 // 422 Free = CR (Pool->FreeList[Index].ForwardLink, POOL_FREE, Link, POOL_FREE_SIGNATURE); 423 RemoveEntryList (&Free->Link); 424 425 Head = (POOL_HEAD *) Free; 426 427 Done: 428 Buffer = NULL; 429 430 if (Head != NULL) { 431 432 // 433 // If we have a pool buffer, fill in the header & tail info 434 // 435 Head->Signature = POOL_HEAD_SIGNATURE; 436 Head->Size = Size; 437 Head->Type = (EFI_MEMORY_TYPE) PoolType; 438 Tail = HEAD_TO_TAIL (Head); 439 Tail->Signature = POOL_TAIL_SIGNATURE; 440 Tail->Size = Size; 441 Buffer = Head->Data; 442 DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD); 443 444 DEBUG (( 445 DEBUG_POOL, 446 "AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType, 447 Buffer, 448 (UINT64)(Size - POOL_OVERHEAD), 449 (UINT64) Pool->Used 450 )); 451 452 // 453 // Account the allocation 454 // 455 Pool->Used += Size; 456 457 } else { 458 DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size)); 459 } 460 461 return Buffer; 462 } 463 464 465 466 /** 467 Frees pool. 468 469 @param Buffer The allocated pool entry to free 470 @param PoolType Pointer to pool type 471 472 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 473 @retval EFI_SUCCESS Pool successfully freed. 474 475 **/ 476 EFI_STATUS 477 EFIAPI 478 CoreInternalFreePool ( 479 IN VOID *Buffer, 480 OUT EFI_MEMORY_TYPE *PoolType OPTIONAL 481 ) 482 { 483 EFI_STATUS Status; 484 485 if (Buffer == NULL) { 486 return EFI_INVALID_PARAMETER; 487 } 488 489 CoreAcquireMemoryLock (); 490 Status = CoreFreePoolI (Buffer, PoolType); 491 CoreReleaseMemoryLock (); 492 return Status; 493 } 494 495 /** 496 Frees pool. 497 498 @param Buffer The allocated pool entry to free 499 500 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 501 @retval EFI_SUCCESS Pool successfully freed. 502 503 **/ 504 EFI_STATUS 505 EFIAPI 506 CoreFreePool ( 507 IN VOID *Buffer 508 ) 509 { 510 EFI_STATUS Status; 511 EFI_MEMORY_TYPE PoolType; 512 513 Status = CoreInternalFreePool (Buffer, &PoolType); 514 if (!EFI_ERROR (Status)) { 515 CoreUpdateProfile ( 516 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 517 MemoryProfileActionFreePool, 518 PoolType, 519 0, 520 Buffer, 521 NULL 522 ); 523 InstallMemoryAttributesTableOnMemoryAllocation (PoolType); 524 } 525 return Status; 526 } 527 528 /** 529 Internal function to free a pool entry. 530 Caller must have the memory lock held 531 532 @param Buffer The allocated pool entry to free 533 @param PoolType Pointer to pool type 534 535 @retval EFI_INVALID_PARAMETER Buffer not valid 536 @retval EFI_SUCCESS Buffer successfully freed. 537 538 **/ 539 EFI_STATUS 540 CoreFreePoolI ( 541 IN VOID *Buffer, 542 OUT EFI_MEMORY_TYPE *PoolType OPTIONAL 543 ) 544 { 545 POOL *Pool; 546 POOL_HEAD *Head; 547 POOL_TAIL *Tail; 548 POOL_FREE *Free; 549 UINTN Index; 550 UINTN NoPages; 551 UINTN Size; 552 CHAR8 *NewPage; 553 UINTN Offset; 554 BOOLEAN AllFree; 555 UINTN Granularity; 556 557 ASSERT(Buffer != NULL); 558 // 559 // Get the head & tail of the pool entry 560 // 561 Head = CR (Buffer, POOL_HEAD, Data, POOL_HEAD_SIGNATURE); 562 ASSERT(Head != NULL); 563 564 if (Head->Signature != POOL_HEAD_SIGNATURE) { 565 return EFI_INVALID_PARAMETER; 566 } 567 568 Tail = HEAD_TO_TAIL (Head); 569 ASSERT(Tail != NULL); 570 571 // 572 // Debug 573 // 574 ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE); 575 ASSERT (Head->Size == Tail->Size); 576 ASSERT_LOCKED (&gMemoryLock); 577 578 if (Tail->Signature != POOL_TAIL_SIGNATURE) { 579 return EFI_INVALID_PARAMETER; 580 } 581 582 if (Head->Size != Tail->Size) { 583 return EFI_INVALID_PARAMETER; 584 } 585 586 // 587 // Determine the pool type and account for it 588 // 589 Size = Head->Size; 590 Pool = LookupPoolHead (Head->Type); 591 if (Pool == NULL) { 592 return EFI_INVALID_PARAMETER; 593 } 594 Pool->Used -= Size; 595 DEBUG ((DEBUG_POOL, "FreePool: %p (len %lx) %,ld\n", Head->Data, (UINT64)(Head->Size - POOL_OVERHEAD), (UINT64) Pool->Used)); 596 597 if (Head->Type == EfiACPIReclaimMemory || 598 Head->Type == EfiACPIMemoryNVS || 599 Head->Type == EfiRuntimeServicesCode || 600 Head->Type == EfiRuntimeServicesData) { 601 602 Granularity = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT; 603 } else { 604 Granularity = DEFAULT_PAGE_ALLOCATION; 605 } 606 607 if (PoolType != NULL) { 608 *PoolType = Head->Type; 609 } 610 611 // 612 // Determine the pool list 613 // 614 Index = SIZE_TO_LIST(Size); 615 DEBUG_CLEAR_MEMORY (Head, Size); 616 617 // 618 // If it's not on the list, it must be pool pages 619 // 620 if (Index >= SIZE_TO_LIST (Granularity)) { 621 622 // 623 // Return the memory pages back to free memory 624 // 625 NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1; 626 NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1); 627 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages); 628 629 } else { 630 631 // 632 // Put the pool entry onto the free pool list 633 // 634 Free = (POOL_FREE *) Head; 635 ASSERT(Free != NULL); 636 Free->Signature = POOL_FREE_SIGNATURE; 637 Free->Index = (UINT32)Index; 638 InsertHeadList (&Pool->FreeList[Index], &Free->Link); 639 640 // 641 // See if all the pool entries in the same page as Free are freed pool 642 // entries 643 // 644 NewPage = (CHAR8 *)((UINTN)Free & ~(Granularity - 1)); 645 Free = (POOL_FREE *) &NewPage[0]; 646 ASSERT(Free != NULL); 647 648 if (Free->Signature == POOL_FREE_SIGNATURE) { 649 650 AllFree = TRUE; 651 Offset = 0; 652 653 while ((Offset < Granularity) && (AllFree)) { 654 Free = (POOL_FREE *) &NewPage[Offset]; 655 ASSERT(Free != NULL); 656 if (Free->Signature != POOL_FREE_SIGNATURE) { 657 AllFree = FALSE; 658 } 659 Offset += LIST_TO_SIZE(Free->Index); 660 } 661 662 if (AllFree) { 663 664 // 665 // All of the pool entries in the same page as Free are free pool 666 // entries 667 // Remove all of these pool entries from the free loop lists. 668 // 669 Free = (POOL_FREE *) &NewPage[0]; 670 ASSERT(Free != NULL); 671 Offset = 0; 672 673 while (Offset < Granularity) { 674 Free = (POOL_FREE *) &NewPage[Offset]; 675 ASSERT(Free != NULL); 676 RemoveEntryList (&Free->Link); 677 Offset += LIST_TO_SIZE(Free->Index); 678 } 679 680 // 681 // Free the page 682 // 683 CoreFreePoolPages ((EFI_PHYSICAL_ADDRESS) (UINTN)NewPage, EFI_SIZE_TO_PAGES (Granularity)); 684 } 685 } 686 } 687 688 // 689 // If this is an OS/OEM specific memory type, then check to see if the last 690 // portion of that memory type has been freed. If it has, then free the 691 // list entry for that memory type 692 // 693 if (((UINT32) Pool->MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) && Pool->Used == 0) { 694 RemoveEntryList (&Pool->Link); 695 CoreFreePoolI (Pool, NULL); 696 } 697 698 return EFI_SUCCESS; 699 } 700 701