1 /** @file 2 SMM Memory page management functions. 3 4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials are licensed and made available 6 under the terms and conditions of the BSD License which accompanies this 7 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 "PiSmmCore.h" 16 #include <Library/SmmServicesTableLib.h> 17 18 #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) 19 20 LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap); 21 22 // 23 // For GetMemoryMap() 24 // 25 26 #define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p') 27 typedef struct { 28 UINTN Signature; 29 LIST_ENTRY Link; 30 31 BOOLEAN FromStack; 32 EFI_MEMORY_TYPE Type; 33 UINT64 Start; 34 UINT64 End; 35 36 } MEMORY_MAP; 37 38 LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap); 39 40 41 #define MAX_MAP_DEPTH 6 42 43 /// 44 /// mMapDepth - depth of new descriptor stack 45 /// 46 UINTN mMapDepth = 0; 47 /// 48 /// mMapStack - space to use as temp storage to build new map descriptors 49 /// 50 MEMORY_MAP mMapStack[MAX_MAP_DEPTH]; 51 UINTN mFreeMapStack = 0; 52 /// 53 /// This list maintain the free memory map list 54 /// 55 LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList); 56 57 /** 58 Allocates pages from the memory map. 59 60 @param[in] Type The type of allocation to perform. 61 @param[in] MemoryType The type of memory to turn the allocated pages 62 into. 63 @param[in] NumberOfPages The number of pages to allocate. 64 @param[out] Memory A pointer to receive the base allocated memory 65 address. 66 @param[in] AddRegion If this memory is new added region. 67 68 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 69 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 70 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 71 @retval EFI_SUCCESS Pages successfully allocated. 72 73 **/ 74 EFI_STATUS 75 SmmInternalAllocatePagesEx ( 76 IN EFI_ALLOCATE_TYPE Type, 77 IN EFI_MEMORY_TYPE MemoryType, 78 IN UINTN NumberOfPages, 79 OUT EFI_PHYSICAL_ADDRESS *Memory, 80 IN BOOLEAN AddRegion 81 ); 82 83 /** 84 Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList. 85 If the list is emtry, then allocate a new page to refuel the list. 86 Please Note this algorithm to allocate the memory map descriptor has a property 87 that the memory allocated for memory entries always grows, and will never really be freed. 88 89 @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList 90 91 **/ 92 MEMORY_MAP * 93 AllocateMemoryMapEntry ( 94 VOID 95 ) 96 { 97 EFI_PHYSICAL_ADDRESS Mem; 98 EFI_STATUS Status; 99 MEMORY_MAP* FreeDescriptorEntries; 100 MEMORY_MAP* Entry; 101 UINTN Index; 102 103 //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n")); 104 105 if (IsListEmpty (&mFreeMemoryMapEntryList)) { 106 //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n")); 107 // 108 // The list is empty, to allocate one page to refuel the list 109 // 110 Status = SmmInternalAllocatePagesEx ( 111 AllocateAnyPages, 112 EfiRuntimeServicesData, 113 EFI_SIZE_TO_PAGES(DEFAULT_PAGE_ALLOCATION), 114 &Mem, 115 TRUE 116 ); 117 ASSERT_EFI_ERROR (Status); 118 if(!EFI_ERROR (Status)) { 119 FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem; 120 //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries)); 121 // 122 // Enque the free memmory map entries into the list 123 // 124 for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / sizeof(MEMORY_MAP); Index++) { 125 FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE; 126 InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link); 127 } 128 } else { 129 return NULL; 130 } 131 } 132 // 133 // dequeue the first descriptor from the list 134 // 135 Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 136 RemoveEntryList (&Entry->Link); 137 138 return Entry; 139 } 140 141 142 /** 143 Internal function. Moves any memory descriptors that are on the 144 temporary descriptor stack to heap. 145 146 **/ 147 VOID 148 CoreFreeMemoryMapStack ( 149 VOID 150 ) 151 { 152 MEMORY_MAP *Entry; 153 154 // 155 // If already freeing the map stack, then return 156 // 157 if (mFreeMapStack != 0) { 158 ASSERT (FALSE); 159 return ; 160 } 161 162 // 163 // Move the temporary memory descriptor stack into pool 164 // 165 mFreeMapStack += 1; 166 167 while (mMapDepth != 0) { 168 // 169 // Deque an memory map entry from mFreeMemoryMapEntryList 170 // 171 Entry = AllocateMemoryMapEntry (); 172 ASSERT (Entry); 173 174 // 175 // Update to proper entry 176 // 177 mMapDepth -= 1; 178 179 if (mMapStack[mMapDepth].Link.ForwardLink != NULL) { 180 181 CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP)); 182 Entry->FromStack = FALSE; 183 184 // 185 // Move this entry to general memory 186 // 187 InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link); 188 RemoveEntryList (&mMapStack[mMapDepth].Link); 189 mMapStack[mMapDepth].Link.ForwardLink = NULL; 190 } 191 } 192 193 mFreeMapStack -= 1; 194 } 195 196 /** 197 Insert new entry from memory map. 198 199 @param[in] Link The old memory map entry to be linked. 200 @param[in] Start The start address of new memory map entry. 201 @param[in] End The end address of new memory map entry. 202 @param[in] Type The type of new memory map entry. 203 @param[in] Next If new entry is inserted to the next of old entry. 204 @param[in] AddRegion If this memory is new added region. 205 **/ 206 VOID 207 InsertNewEntry ( 208 IN LIST_ENTRY *Link, 209 IN UINT64 Start, 210 IN UINT64 End, 211 IN EFI_MEMORY_TYPE Type, 212 IN BOOLEAN Next, 213 IN BOOLEAN AddRegion 214 ) 215 { 216 MEMORY_MAP *Entry; 217 218 Entry = &mMapStack[mMapDepth]; 219 mMapDepth += 1; 220 ASSERT (mMapDepth < MAX_MAP_DEPTH); 221 Entry->FromStack = TRUE; 222 223 Entry->Signature = MEMORY_MAP_SIGNATURE; 224 Entry->Type = Type; 225 Entry->Start = Start; 226 Entry->End = End; 227 if (Next) { 228 InsertHeadList (Link, &Entry->Link); 229 } else { 230 InsertTailList (Link, &Entry->Link); 231 } 232 } 233 234 /** 235 Remove old entry from memory map. 236 237 @param[in] Entry Memory map entry to be removed. 238 **/ 239 VOID 240 RemoveOldEntry ( 241 IN MEMORY_MAP *Entry 242 ) 243 { 244 RemoveEntryList (&Entry->Link); 245 if (!Entry->FromStack) { 246 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); 247 } 248 } 249 250 /** 251 Update SMM memory map entry. 252 253 @param[in] Type The type of allocation to perform. 254 @param[in] Memory The base of memory address. 255 @param[in] NumberOfPages The number of pages to allocate. 256 @param[in] AddRegion If this memory is new added region. 257 **/ 258 VOID 259 ConvertSmmMemoryMapEntry ( 260 IN EFI_MEMORY_TYPE Type, 261 IN EFI_PHYSICAL_ADDRESS Memory, 262 IN UINTN NumberOfPages, 263 IN BOOLEAN AddRegion 264 ) 265 { 266 LIST_ENTRY *Link; 267 MEMORY_MAP *Entry; 268 MEMORY_MAP *NextEntry; 269 LIST_ENTRY *NextLink; 270 MEMORY_MAP *PreviousEntry; 271 LIST_ENTRY *PreviousLink; 272 EFI_PHYSICAL_ADDRESS Start; 273 EFI_PHYSICAL_ADDRESS End; 274 275 Start = Memory; 276 End = Memory + EFI_PAGES_TO_SIZE(NumberOfPages) - 1; 277 278 // 279 // Exclude memory region 280 // 281 Link = gMemoryMap.ForwardLink; 282 while (Link != &gMemoryMap) { 283 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 284 Link = Link->ForwardLink; 285 286 // 287 // --------------------------------------------------- 288 // | +----------+ +------+ +------+ +------+ | 289 // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- 290 // +----------+ ^ +------+ +------+ +------+ 291 // | 292 // +------+ 293 // |EntryX| 294 // +------+ 295 // 296 if (Entry->Start > End) { 297 if ((Entry->Start == End + 1) && (Entry->Type == Type)) { 298 Entry->Start = Start; 299 return ; 300 } 301 InsertNewEntry ( 302 &Entry->Link, 303 Start, 304 End, 305 Type, 306 FALSE, 307 AddRegion 308 ); 309 return ; 310 } 311 312 if ((Entry->Start <= Start) && (Entry->End >= End)) { 313 if (Entry->Type != Type) { 314 if (Entry->Start < Start) { 315 // 316 // --------------------------------------------------- 317 // | +----------+ +------+ +------+ +------+ | 318 // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- 319 // +----------+ +------+ ^ +------+ +------+ 320 // | 321 // +------+ 322 // |EntryA| 323 // +------+ 324 // 325 InsertNewEntry ( 326 &Entry->Link, 327 Entry->Start, 328 Start - 1, 329 Entry->Type, 330 FALSE, 331 AddRegion 332 ); 333 } 334 if (Entry->End > End) { 335 // 336 // --------------------------------------------------- 337 // | +----------+ +------+ +------+ +------+ | 338 // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- 339 // +----------+ +------+ +------+ ^ +------+ 340 // | 341 // +------+ 342 // |EntryZ| 343 // +------+ 344 // 345 InsertNewEntry ( 346 &Entry->Link, 347 End + 1, 348 Entry->End, 349 Entry->Type, 350 TRUE, 351 AddRegion 352 ); 353 } 354 // 355 // Update this node 356 // 357 Entry->Start = Start; 358 Entry->End = End; 359 Entry->Type = Type; 360 361 // 362 // Check adjacent 363 // 364 NextLink = Entry->Link.ForwardLink; 365 if (NextLink != &gMemoryMap) { 366 NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 367 // 368 // --------------------------------------------------- 369 // | +----------+ +------+ +-----------------+ | 370 // ---|gMemoryMep|---|Entry1|---|EntryX Entry3|--- 371 // +----------+ +------+ +-----------------+ 372 // 373 if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) { 374 Entry->End = NextEntry->End; 375 RemoveOldEntry (NextEntry); 376 } 377 } 378 PreviousLink = Entry->Link.BackLink; 379 if (PreviousLink != &gMemoryMap) { 380 PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 381 // 382 // --------------------------------------------------- 383 // | +----------+ +-----------------+ +------+ | 384 // ---|gMemoryMep|---|Entry1 EntryX|---|Entry3|--- 385 // +----------+ +-----------------+ +------+ 386 // 387 if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) { 388 PreviousEntry->End = Entry->End; 389 RemoveOldEntry (Entry); 390 } 391 } 392 } 393 return ; 394 } 395 } 396 397 // 398 // --------------------------------------------------- 399 // | +----------+ +------+ +------+ +------+ | 400 // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- 401 // +----------+ +------+ +------+ +------+ ^ 402 // | 403 // +------+ 404 // |EntryX| 405 // +------+ 406 // 407 Link = gMemoryMap.BackLink; 408 if (Link != &gMemoryMap) { 409 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 410 if ((Entry->End + 1 == Start) && (Entry->Type == Type)) { 411 Entry->End = End; 412 return ; 413 } 414 } 415 InsertNewEntry ( 416 &gMemoryMap, 417 Start, 418 End, 419 Type, 420 FALSE, 421 AddRegion 422 ); 423 return ; 424 } 425 426 /** 427 Return the count of Smm memory map entry. 428 429 @return The count of Smm memory map entry. 430 **/ 431 UINTN 432 GetSmmMemoryMapEntryCount ( 433 VOID 434 ) 435 { 436 LIST_ENTRY *Link; 437 UINTN Count; 438 439 Count = 0; 440 Link = gMemoryMap.ForwardLink; 441 while (Link != &gMemoryMap) { 442 Link = Link->ForwardLink; 443 Count++; 444 } 445 return Count; 446 } 447 448 /** 449 Dump Smm memory map entry. 450 **/ 451 VOID 452 DumpSmmMemoryMapEntry ( 453 VOID 454 ) 455 { 456 LIST_ENTRY *Link; 457 MEMORY_MAP *Entry; 458 EFI_PHYSICAL_ADDRESS Last; 459 460 Last = 0; 461 DEBUG ((DEBUG_INFO, "DumpSmmMemoryMapEntry:\n")); 462 Link = gMemoryMap.ForwardLink; 463 while (Link != &gMemoryMap) { 464 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 465 Link = Link->ForwardLink; 466 467 if ((Last != 0) && (Last != (UINT64)-1)) { 468 if (Last + 1 != Entry->Start) { 469 Last = (UINT64)-1; 470 } else { 471 Last = Entry->End; 472 } 473 } else if (Last == 0) { 474 Last = Entry->End; 475 } 476 477 DEBUG ((DEBUG_INFO, "Entry (Link - 0x%x)\n", &Entry->Link)); 478 DEBUG ((DEBUG_INFO, " Signature - 0x%x\n", Entry->Signature)); 479 DEBUG ((DEBUG_INFO, " Link.ForwardLink - 0x%x\n", Entry->Link.ForwardLink)); 480 DEBUG ((DEBUG_INFO, " Link.BackLink - 0x%x\n", Entry->Link.BackLink)); 481 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Entry->Type)); 482 DEBUG ((DEBUG_INFO, " Start - 0x%016lx\n", Entry->Start)); 483 DEBUG ((DEBUG_INFO, " End - 0x%016lx\n", Entry->End)); 484 } 485 486 ASSERT (Last != (UINT64)-1); 487 } 488 489 /** 490 Dump Smm memory map. 491 **/ 492 VOID 493 DumpSmmMemoryMap ( 494 VOID 495 ) 496 { 497 LIST_ENTRY *Node; 498 FREE_PAGE_LIST *Pages; 499 500 DEBUG ((DEBUG_INFO, "DumpSmmMemoryMap\n")); 501 502 Pages = NULL; 503 Node = mSmmMemoryMap.ForwardLink; 504 while (Node != &mSmmMemoryMap) { 505 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 506 DEBUG ((DEBUG_INFO, "Pages - 0x%x\n", Pages)); 507 DEBUG ((DEBUG_INFO, "Pages->NumberOfPages - 0x%x\n", Pages->NumberOfPages)); 508 Node = Node->ForwardLink; 509 } 510 } 511 512 /** 513 Check if a Smm base~length is in Smm memory map. 514 515 @param[in] Base The base address of Smm memory to be checked. 516 @param[in] Length THe length of Smm memory to be checked. 517 518 @retval TRUE Smm base~length is in smm memory map. 519 @retval FALSE Smm base~length is in smm memory map. 520 **/ 521 BOOLEAN 522 SmmMemoryMapConsistencyCheckRange ( 523 IN EFI_PHYSICAL_ADDRESS Base, 524 IN UINTN Length 525 ) 526 { 527 LIST_ENTRY *Link; 528 MEMORY_MAP *Entry; 529 BOOLEAN Result; 530 531 Result = FALSE; 532 Link = gMemoryMap.ForwardLink; 533 while (Link != &gMemoryMap) { 534 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 535 Link = Link->ForwardLink; 536 537 if (Entry->Type != EfiConventionalMemory) { 538 continue; 539 } 540 if (Entry->Start == Base && Entry->End == Base + Length - 1) { 541 Result = TRUE; 542 break; 543 } 544 } 545 546 return Result; 547 } 548 549 /** 550 Check the consistency of Smm memory map. 551 **/ 552 VOID 553 SmmMemoryMapConsistencyCheck ( 554 VOID 555 ) 556 { 557 LIST_ENTRY *Node; 558 FREE_PAGE_LIST *Pages; 559 BOOLEAN Result; 560 561 Pages = NULL; 562 Node = mSmmMemoryMap.ForwardLink; 563 while (Node != &mSmmMemoryMap) { 564 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 565 Result = SmmMemoryMapConsistencyCheckRange ((EFI_PHYSICAL_ADDRESS)(UINTN)Pages, (UINTN)EFI_PAGES_TO_SIZE(Pages->NumberOfPages)); 566 ASSERT (Result); 567 Node = Node->ForwardLink; 568 } 569 } 570 571 /** 572 Internal Function. Allocate n pages from given free page node. 573 574 @param Pages The free page node. 575 @param NumberOfPages Number of pages to be allocated. 576 @param MaxAddress Request to allocate memory below this address. 577 578 @return Memory address of allocated pages. 579 580 **/ 581 UINTN 582 InternalAllocPagesOnOneNode ( 583 IN OUT FREE_PAGE_LIST *Pages, 584 IN UINTN NumberOfPages, 585 IN UINTN MaxAddress 586 ) 587 { 588 UINTN Top; 589 UINTN Bottom; 590 FREE_PAGE_LIST *Node; 591 592 Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages); 593 if (Top > Pages->NumberOfPages) { 594 Top = Pages->NumberOfPages; 595 } 596 Bottom = Top - NumberOfPages; 597 598 if (Top < Pages->NumberOfPages) { 599 Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top)); 600 Node->NumberOfPages = Pages->NumberOfPages - Top; 601 InsertHeadList (&Pages->Link, &Node->Link); 602 } 603 604 if (Bottom > 0) { 605 Pages->NumberOfPages = Bottom; 606 } else { 607 RemoveEntryList (&Pages->Link); 608 } 609 610 return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom); 611 } 612 613 /** 614 Internal Function. Allocate n pages from free page list below MaxAddress. 615 616 @param FreePageList The free page node. 617 @param NumberOfPages Number of pages to be allocated. 618 @param MaxAddress Request to allocate memory below this address. 619 620 @return Memory address of allocated pages. 621 622 **/ 623 UINTN 624 InternalAllocMaxAddress ( 625 IN OUT LIST_ENTRY *FreePageList, 626 IN UINTN NumberOfPages, 627 IN UINTN MaxAddress 628 ) 629 { 630 LIST_ENTRY *Node; 631 FREE_PAGE_LIST *Pages; 632 633 for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { 634 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 635 if (Pages->NumberOfPages >= NumberOfPages && 636 (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) { 637 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress); 638 } 639 } 640 return (UINTN)(-1); 641 } 642 643 /** 644 Internal Function. Allocate n pages from free page list at given address. 645 646 @param FreePageList The free page node. 647 @param NumberOfPages Number of pages to be allocated. 648 @param MaxAddress Request to allocate memory below this address. 649 650 @return Memory address of allocated pages. 651 652 **/ 653 UINTN 654 InternalAllocAddress ( 655 IN OUT LIST_ENTRY *FreePageList, 656 IN UINTN NumberOfPages, 657 IN UINTN Address 658 ) 659 { 660 UINTN EndAddress; 661 LIST_ENTRY *Node; 662 FREE_PAGE_LIST *Pages; 663 664 if ((Address & EFI_PAGE_MASK) != 0) { 665 return ~Address; 666 } 667 668 EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages); 669 for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) { 670 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 671 if ((UINTN)Pages <= Address) { 672 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) { 673 break; 674 } 675 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress); 676 } 677 } 678 return ~Address; 679 } 680 681 /** 682 Allocates pages from the memory map. 683 684 @param[in] Type The type of allocation to perform. 685 @param[in] MemoryType The type of memory to turn the allocated pages 686 into. 687 @param[in] NumberOfPages The number of pages to allocate. 688 @param[out] Memory A pointer to receive the base allocated memory 689 address. 690 @param[in] AddRegion If this memory is new added region. 691 692 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 693 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 694 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 695 @retval EFI_SUCCESS Pages successfully allocated. 696 697 **/ 698 EFI_STATUS 699 SmmInternalAllocatePagesEx ( 700 IN EFI_ALLOCATE_TYPE Type, 701 IN EFI_MEMORY_TYPE MemoryType, 702 IN UINTN NumberOfPages, 703 OUT EFI_PHYSICAL_ADDRESS *Memory, 704 IN BOOLEAN AddRegion 705 ) 706 { 707 UINTN RequestedAddress; 708 709 if (MemoryType != EfiRuntimeServicesCode && 710 MemoryType != EfiRuntimeServicesData) { 711 return EFI_INVALID_PARAMETER; 712 } 713 714 if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) { 715 return EFI_OUT_OF_RESOURCES; 716 } 717 718 // 719 // We don't track memory type in SMM 720 // 721 RequestedAddress = (UINTN)*Memory; 722 switch (Type) { 723 case AllocateAnyPages: 724 RequestedAddress = (UINTN)(-1); 725 case AllocateMaxAddress: 726 *Memory = InternalAllocMaxAddress ( 727 &mSmmMemoryMap, 728 NumberOfPages, 729 RequestedAddress 730 ); 731 if (*Memory == (UINTN)-1) { 732 return EFI_OUT_OF_RESOURCES; 733 } 734 break; 735 case AllocateAddress: 736 *Memory = InternalAllocAddress ( 737 &mSmmMemoryMap, 738 NumberOfPages, 739 RequestedAddress 740 ); 741 if (*Memory != RequestedAddress) { 742 return EFI_NOT_FOUND; 743 } 744 break; 745 default: 746 return EFI_INVALID_PARAMETER; 747 } 748 749 // 750 // Update SmmMemoryMap here. 751 // 752 ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion); 753 if (!AddRegion) { 754 CoreFreeMemoryMapStack(); 755 } 756 757 return EFI_SUCCESS; 758 } 759 760 /** 761 Allocates pages from the memory map. 762 763 @param[in] Type The type of allocation to perform. 764 @param[in] MemoryType The type of memory to turn the allocated pages 765 into. 766 @param[in] NumberOfPages The number of pages to allocate. 767 @param[out] Memory A pointer to receive the base allocated memory 768 address. 769 770 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 771 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 772 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 773 @retval EFI_SUCCESS Pages successfully allocated. 774 775 **/ 776 EFI_STATUS 777 EFIAPI 778 SmmInternalAllocatePages ( 779 IN EFI_ALLOCATE_TYPE Type, 780 IN EFI_MEMORY_TYPE MemoryType, 781 IN UINTN NumberOfPages, 782 OUT EFI_PHYSICAL_ADDRESS *Memory 783 ) 784 { 785 return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory, FALSE); 786 } 787 788 /** 789 Allocates pages from the memory map. 790 791 @param Type The type of allocation to perform. 792 @param MemoryType The type of memory to turn the allocated pages 793 into. 794 @param NumberOfPages The number of pages to allocate. 795 @param Memory A pointer to receive the base allocated memory 796 address. 797 798 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 799 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 800 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 801 @retval EFI_SUCCESS Pages successfully allocated. 802 803 **/ 804 EFI_STATUS 805 EFIAPI 806 SmmAllocatePages ( 807 IN EFI_ALLOCATE_TYPE Type, 808 IN EFI_MEMORY_TYPE MemoryType, 809 IN UINTN NumberOfPages, 810 OUT EFI_PHYSICAL_ADDRESS *Memory 811 ) 812 { 813 EFI_STATUS Status; 814 815 Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory); 816 if (!EFI_ERROR (Status)) { 817 SmmCoreUpdateProfile ( 818 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 819 MemoryProfileActionAllocatePages, 820 MemoryType, 821 EFI_PAGES_TO_SIZE (NumberOfPages), 822 (VOID *) (UINTN) *Memory, 823 NULL 824 ); 825 } 826 return Status; 827 } 828 829 /** 830 Internal Function. Merge two adjacent nodes. 831 832 @param First The first of two nodes to merge. 833 834 @return Pointer to node after merge (if success) or pointer to next node (if fail). 835 836 **/ 837 FREE_PAGE_LIST * 838 InternalMergeNodes ( 839 IN FREE_PAGE_LIST *First 840 ) 841 { 842 FREE_PAGE_LIST *Next; 843 844 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link); 845 ASSERT ( 846 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages); 847 848 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) { 849 First->NumberOfPages += Next->NumberOfPages; 850 RemoveEntryList (&Next->Link); 851 Next = First; 852 } 853 return Next; 854 } 855 856 /** 857 Frees previous allocated pages. 858 859 @param[in] Memory Base address of memory being freed. 860 @param[in] NumberOfPages The number of pages to free. 861 @param[in] AddRegion If this memory is new added region. 862 863 @retval EFI_NOT_FOUND Could not find the entry that covers the range. 864 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero. 865 @return EFI_SUCCESS Pages successfully freed. 866 867 **/ 868 EFI_STATUS 869 SmmInternalFreePagesEx ( 870 IN EFI_PHYSICAL_ADDRESS Memory, 871 IN UINTN NumberOfPages, 872 IN BOOLEAN AddRegion 873 ) 874 { 875 LIST_ENTRY *Node; 876 FREE_PAGE_LIST *Pages; 877 878 if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) { 879 return EFI_INVALID_PARAMETER; 880 } 881 882 Pages = NULL; 883 Node = mSmmMemoryMap.ForwardLink; 884 while (Node != &mSmmMemoryMap) { 885 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 886 if (Memory < (UINTN)Pages) { 887 break; 888 } 889 Node = Node->ForwardLink; 890 } 891 892 if (Node != &mSmmMemoryMap && 893 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) { 894 return EFI_INVALID_PARAMETER; 895 } 896 897 if (Node->BackLink != &mSmmMemoryMap) { 898 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link); 899 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) { 900 return EFI_INVALID_PARAMETER; 901 } 902 } 903 904 Pages = (FREE_PAGE_LIST*)(UINTN)Memory; 905 Pages->NumberOfPages = NumberOfPages; 906 InsertTailList (Node, &Pages->Link); 907 908 if (Pages->Link.BackLink != &mSmmMemoryMap) { 909 Pages = InternalMergeNodes ( 910 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) 911 ); 912 } 913 914 if (Node != &mSmmMemoryMap) { 915 InternalMergeNodes (Pages); 916 } 917 918 // 919 // Update SmmMemoryMap here. 920 // 921 ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion); 922 if (!AddRegion) { 923 CoreFreeMemoryMapStack(); 924 } 925 926 return EFI_SUCCESS; 927 } 928 929 /** 930 Frees previous allocated pages. 931 932 @param[in] Memory Base address of memory being freed. 933 @param[in] NumberOfPages The number of pages to free. 934 935 @retval EFI_NOT_FOUND Could not find the entry that covers the range. 936 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero. 937 @return EFI_SUCCESS Pages successfully freed. 938 939 **/ 940 EFI_STATUS 941 EFIAPI 942 SmmInternalFreePages ( 943 IN EFI_PHYSICAL_ADDRESS Memory, 944 IN UINTN NumberOfPages 945 ) 946 { 947 return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE); 948 } 949 950 /** 951 Frees previous allocated pages. 952 953 @param Memory Base address of memory being freed. 954 @param NumberOfPages The number of pages to free. 955 956 @retval EFI_NOT_FOUND Could not find the entry that covers the range. 957 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero. 958 @return EFI_SUCCESS Pages successfully freed. 959 960 **/ 961 EFI_STATUS 962 EFIAPI 963 SmmFreePages ( 964 IN EFI_PHYSICAL_ADDRESS Memory, 965 IN UINTN NumberOfPages 966 ) 967 { 968 EFI_STATUS Status; 969 970 Status = SmmInternalFreePages (Memory, NumberOfPages); 971 if (!EFI_ERROR (Status)) { 972 SmmCoreUpdateProfile ( 973 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 974 MemoryProfileActionFreePages, 975 EfiMaxMemoryType, 976 EFI_PAGES_TO_SIZE (NumberOfPages), 977 (VOID *) (UINTN) Memory, 978 NULL 979 ); 980 } 981 return Status; 982 } 983 984 /** 985 Add free SMRAM region for use by memory service. 986 987 @param MemBase Base address of memory region. 988 @param MemLength Length of the memory region. 989 @param Type Memory type. 990 @param Attributes Memory region state. 991 992 **/ 993 VOID 994 SmmAddMemoryRegion ( 995 IN EFI_PHYSICAL_ADDRESS MemBase, 996 IN UINT64 MemLength, 997 IN EFI_MEMORY_TYPE Type, 998 IN UINT64 Attributes 999 ) 1000 { 1001 UINTN AlignedMemBase; 1002 1003 // 1004 // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization 1005 // 1006 if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { 1007 Type = EfiRuntimeServicesData; 1008 } else { 1009 Type = EfiConventionalMemory; 1010 } 1011 1012 DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n")); 1013 DEBUG ((DEBUG_INFO, " MemBase - 0x%lx\n", MemBase)); 1014 DEBUG ((DEBUG_INFO, " MemLength - 0x%lx\n", MemLength)); 1015 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Type)); 1016 DEBUG ((DEBUG_INFO, " Attributes - 0x%lx\n", Attributes)); 1017 1018 // 1019 // Align range on an EFI_PAGE_SIZE boundary 1020 // 1021 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; 1022 MemLength -= AlignedMemBase - MemBase; 1023 if (Type == EfiConventionalMemory) { 1024 SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); 1025 } else { 1026 ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); 1027 } 1028 1029 CoreFreeMemoryMapStack (); 1030 } 1031 1032 /** 1033 This function returns a copy of the current memory map. The map is an array of 1034 memory descriptors, each of which describes a contiguous block of memory. 1035 1036 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the 1037 MemoryMap buffer. On input, this is the size of 1038 the buffer allocated by the caller. On output, 1039 it is the size of the buffer returned by the 1040 firmware if the buffer was large enough, or the 1041 size of the buffer needed to contain the map if 1042 the buffer was too small. 1043 @param[in, out] MemoryMap A pointer to the buffer in which firmware places 1044 the current memory map. 1045 @param[out] MapKey A pointer to the location in which firmware 1046 returns the key for the current memory map. 1047 @param[out] DescriptorSize A pointer to the location in which firmware 1048 returns the size, in bytes, of an individual 1049 EFI_MEMORY_DESCRIPTOR. 1050 @param[out] DescriptorVersion A pointer to the location in which firmware 1051 returns the version number associated with the 1052 EFI_MEMORY_DESCRIPTOR. 1053 1054 @retval EFI_SUCCESS The memory map was returned in the MemoryMap 1055 buffer. 1056 @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current 1057 buffer size needed to hold the memory map is 1058 returned in MemoryMapSize. 1059 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. 1060 1061 **/ 1062 EFI_STATUS 1063 EFIAPI 1064 SmmCoreGetMemoryMap ( 1065 IN OUT UINTN *MemoryMapSize, 1066 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, 1067 OUT UINTN *MapKey, 1068 OUT UINTN *DescriptorSize, 1069 OUT UINT32 *DescriptorVersion 1070 ) 1071 { 1072 UINTN Count; 1073 LIST_ENTRY *Link; 1074 MEMORY_MAP *Entry; 1075 UINTN Size; 1076 UINTN BufferSize; 1077 1078 Size = sizeof (EFI_MEMORY_DESCRIPTOR); 1079 1080 // 1081 // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will 1082 // prevent people from having pointer math bugs in their code. 1083 // now you have to use *DescriptorSize to make things work. 1084 // 1085 Size += sizeof(UINT64) - (Size % sizeof (UINT64)); 1086 1087 if (DescriptorSize != NULL) { 1088 *DescriptorSize = Size; 1089 } 1090 1091 if (DescriptorVersion != NULL) { 1092 *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION; 1093 } 1094 1095 Count = GetSmmMemoryMapEntryCount (); 1096 BufferSize = Size * Count; 1097 if (*MemoryMapSize < BufferSize) { 1098 *MemoryMapSize = BufferSize; 1099 return EFI_BUFFER_TOO_SMALL; 1100 } 1101 1102 *MemoryMapSize = BufferSize; 1103 if (MemoryMap == NULL) { 1104 return EFI_INVALID_PARAMETER; 1105 } 1106 1107 ZeroMem (MemoryMap, BufferSize); 1108 Link = gMemoryMap.ForwardLink; 1109 while (Link != &gMemoryMap) { 1110 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); 1111 Link = Link->ForwardLink; 1112 1113 MemoryMap->Type = Entry->Type; 1114 MemoryMap->PhysicalStart = Entry->Start; 1115 MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT); 1116 1117 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); 1118 } 1119 1120 return EFI_SUCCESS; 1121 } 1122