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 673