1 /** @file 2 3 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> 4 This program and the accompanying materials 5 are licensed and made available under the terms and conditions of the BSD License 6 which accompanies this distribution. The full text of the license may be found at 7 http://opensource.org/licenses/bsd-license.php 8 9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12 **/ 13 14 #include "PiSmmCpuDxeSmm.h" 15 16 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ 17 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size))) 18 19 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ 20 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) 21 22 EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap; 23 UINTN mUefiMemoryMapSize; 24 UINTN mUefiDescriptorSize; 25 26 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { 27 {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, 28 {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, 29 {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, 30 }; 31 32 /** 33 Return page table base. 34 35 @return page table base. 36 **/ 37 UINTN 38 GetPageTableBase ( 39 VOID 40 ) 41 { 42 return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); 43 } 44 45 /** 46 Return length according to page attributes. 47 48 @param[in] PageAttributes The page attribute of the page entry. 49 50 @return The length of page entry. 51 **/ 52 UINTN 53 PageAttributeToLength ( 54 IN PAGE_ATTRIBUTE PageAttribute 55 ) 56 { 57 UINTN Index; 58 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { 59 if (PageAttribute == mPageAttributeTable[Index].Attribute) { 60 return (UINTN)mPageAttributeTable[Index].Length; 61 } 62 } 63 return 0; 64 } 65 66 /** 67 Return address mask according to page attributes. 68 69 @param[in] PageAttributes The page attribute of the page entry. 70 71 @return The address mask of page entry. 72 **/ 73 UINTN 74 PageAttributeToMask ( 75 IN PAGE_ATTRIBUTE PageAttribute 76 ) 77 { 78 UINTN Index; 79 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { 80 if (PageAttribute == mPageAttributeTable[Index].Attribute) { 81 return (UINTN)mPageAttributeTable[Index].AddressMask; 82 } 83 } 84 return 0; 85 } 86 87 /** 88 Return page table entry to match the address. 89 90 @param[in] Address The address to be checked. 91 @param[out] PageAttributes The page attribute of the page entry. 92 93 @return The page entry. 94 **/ 95 VOID * 96 GetPageTableEntry ( 97 IN PHYSICAL_ADDRESS Address, 98 OUT PAGE_ATTRIBUTE *PageAttribute 99 ) 100 { 101 UINTN Index1; 102 UINTN Index2; 103 UINTN Index3; 104 UINTN Index4; 105 UINT64 *L1PageTable; 106 UINT64 *L2PageTable; 107 UINT64 *L3PageTable; 108 UINT64 *L4PageTable; 109 110 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK; 111 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK; 112 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK; 113 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK; 114 115 if (sizeof(UINTN) == sizeof(UINT64)) { 116 L4PageTable = (UINT64 *)GetPageTableBase (); 117 if (L4PageTable[Index4] == 0) { 118 *PageAttribute = PageNone; 119 return NULL; 120 } 121 122 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64); 123 } else { 124 L3PageTable = (UINT64 *)GetPageTableBase (); 125 } 126 if (L3PageTable[Index3] == 0) { 127 *PageAttribute = PageNone; 128 return NULL; 129 } 130 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { 131 // 1G 132 *PageAttribute = Page1G; 133 return &L3PageTable[Index3]; 134 } 135 136 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64); 137 if (L2PageTable[Index2] == 0) { 138 *PageAttribute = PageNone; 139 return NULL; 140 } 141 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { 142 // 2M 143 *PageAttribute = Page2M; 144 return &L2PageTable[Index2]; 145 } 146 147 // 4k 148 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64); 149 if ((L1PageTable[Index1] == 0) && (Address != 0)) { 150 *PageAttribute = PageNone; 151 return NULL; 152 } 153 *PageAttribute = Page4K; 154 return &L1PageTable[Index1]; 155 } 156 157 /** 158 Return memory attributes of page entry. 159 160 @param[in] PageEntry The page entry. 161 162 @return Memory attributes of page entry. 163 **/ 164 UINT64 165 GetAttributesFromPageEntry ( 166 IN UINT64 *PageEntry 167 ) 168 { 169 UINT64 Attributes; 170 Attributes = 0; 171 if ((*PageEntry & IA32_PG_P) == 0) { 172 Attributes |= EFI_MEMORY_RP; 173 } 174 if ((*PageEntry & IA32_PG_RW) == 0) { 175 Attributes |= EFI_MEMORY_RO; 176 } 177 if ((*PageEntry & IA32_PG_NX) != 0) { 178 Attributes |= EFI_MEMORY_XP; 179 } 180 return Attributes; 181 } 182 183 /** 184 Modify memory attributes of page entry. 185 186 @param[in] PageEntry The page entry. 187 @param[in] Attributes The bit mask of attributes to modify for the memory region. 188 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes. 189 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. 190 **/ 191 VOID 192 ConvertPageEntryAttribute ( 193 IN UINT64 *PageEntry, 194 IN UINT64 Attributes, 195 IN BOOLEAN IsSet, 196 OUT BOOLEAN *IsModified 197 ) 198 { 199 UINT64 CurrentPageEntry; 200 UINT64 NewPageEntry; 201 202 CurrentPageEntry = *PageEntry; 203 NewPageEntry = CurrentPageEntry; 204 if ((Attributes & EFI_MEMORY_RP) != 0) { 205 if (IsSet) { 206 NewPageEntry &= ~(UINT64)IA32_PG_P; 207 } else { 208 NewPageEntry |= IA32_PG_P; 209 } 210 } 211 if ((Attributes & EFI_MEMORY_RO) != 0) { 212 if (IsSet) { 213 NewPageEntry &= ~(UINT64)IA32_PG_RW; 214 } else { 215 NewPageEntry |= IA32_PG_RW; 216 } 217 } 218 if ((Attributes & EFI_MEMORY_XP) != 0) { 219 if (mXdSupported) { 220 if (IsSet) { 221 NewPageEntry |= IA32_PG_NX; 222 } else { 223 NewPageEntry &= ~IA32_PG_NX; 224 } 225 } 226 } 227 *PageEntry = NewPageEntry; 228 if (CurrentPageEntry != NewPageEntry) { 229 *IsModified = TRUE; 230 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry)); 231 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry)); 232 } else { 233 *IsModified = FALSE; 234 } 235 } 236 237 /** 238 This function returns if there is need to split page entry. 239 240 @param[in] BaseAddress The base address to be checked. 241 @param[in] Length The length to be checked. 242 @param[in] PageEntry The page entry to be checked. 243 @param[in] PageAttribute The page attribute of the page entry. 244 245 @retval SplitAttributes on if there is need to split page entry. 246 **/ 247 PAGE_ATTRIBUTE 248 NeedSplitPage ( 249 IN PHYSICAL_ADDRESS BaseAddress, 250 IN UINT64 Length, 251 IN UINT64 *PageEntry, 252 IN PAGE_ATTRIBUTE PageAttribute 253 ) 254 { 255 UINT64 PageEntryLength; 256 257 PageEntryLength = PageAttributeToLength (PageAttribute); 258 259 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) { 260 return PageNone; 261 } 262 263 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) { 264 return Page4K; 265 } 266 267 return Page2M; 268 } 269 270 /** 271 This function splits one page entry to small page entries. 272 273 @param[in] PageEntry The page entry to be splitted. 274 @param[in] PageAttribute The page attribute of the page entry. 275 @param[in] SplitAttribute How to split the page entry. 276 277 @retval RETURN_SUCCESS The page entry is splitted. 278 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted. 279 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. 280 **/ 281 RETURN_STATUS 282 SplitPage ( 283 IN UINT64 *PageEntry, 284 IN PAGE_ATTRIBUTE PageAttribute, 285 IN PAGE_ATTRIBUTE SplitAttribute 286 ) 287 { 288 UINT64 BaseAddress; 289 UINT64 *NewPageEntry; 290 UINTN Index; 291 292 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G); 293 294 if (PageAttribute == Page2M) { 295 // 296 // Split 2M to 4K 297 // 298 ASSERT (SplitAttribute == Page4K); 299 if (SplitAttribute == Page4K) { 300 NewPageEntry = AllocatePageTableMemory (1); 301 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); 302 if (NewPageEntry == NULL) { 303 return RETURN_OUT_OF_RESOURCES; 304 } 305 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64; 306 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { 307 NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index + ((*PageEntry) & PAGE_PROGATE_BITS); 308 } 309 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS; 310 return RETURN_SUCCESS; 311 } else { 312 return RETURN_UNSUPPORTED; 313 } 314 } else if (PageAttribute == Page1G) { 315 // 316 // Split 1G to 2M 317 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table. 318 // 319 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); 320 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { 321 NewPageEntry = AllocatePageTableMemory (1); 322 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); 323 if (NewPageEntry == NULL) { 324 return RETURN_OUT_OF_RESOURCES; 325 } 326 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64; 327 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { 328 NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS); 329 } 330 (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS; 331 return RETURN_SUCCESS; 332 } else { 333 return RETURN_UNSUPPORTED; 334 } 335 } else { 336 return RETURN_UNSUPPORTED; 337 } 338 } 339 340 /** 341 This function modifies the page attributes for the memory region specified by BaseAddress and 342 Length from their current attributes to the attributes specified by Attributes. 343 344 Caller should make sure BaseAddress and Length is at page boundary. 345 346 @param[in] BaseAddress The physical address that is the start address of a memory region. 347 @param[in] Length The size in bytes of the memory region. 348 @param[in] Attributes The bit mask of attributes to modify for the memory region. 349 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes. 350 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. 351 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. 352 353 @retval RETURN_SUCCESS The attributes were modified for the memory region. 354 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by 355 BaseAddress and Length cannot be modified. 356 @retval RETURN_INVALID_PARAMETER Length is zero. 357 Attributes specified an illegal combination of attributes that 358 cannot be set together. 359 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of 360 the memory resource range. 361 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory 362 resource range specified by BaseAddress and Length. 363 The bit mask of attributes is not support for the memory resource 364 range specified by BaseAddress and Length. 365 **/ 366 RETURN_STATUS 367 EFIAPI 368 ConvertMemoryPageAttributes ( 369 IN PHYSICAL_ADDRESS BaseAddress, 370 IN UINT64 Length, 371 IN UINT64 Attributes, 372 IN BOOLEAN IsSet, 373 OUT BOOLEAN *IsSplitted, OPTIONAL 374 OUT BOOLEAN *IsModified OPTIONAL 375 ) 376 { 377 UINT64 *PageEntry; 378 PAGE_ATTRIBUTE PageAttribute; 379 UINTN PageEntryLength; 380 PAGE_ATTRIBUTE SplitAttribute; 381 RETURN_STATUS Status; 382 BOOLEAN IsEntryModified; 383 384 ASSERT (Attributes != 0); 385 ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0); 386 387 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); 388 ASSERT ((Length & (SIZE_4KB - 1)) == 0); 389 390 if (Length == 0) { 391 return RETURN_INVALID_PARAMETER; 392 } 393 394 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes)); 395 396 if (IsSplitted != NULL) { 397 *IsSplitted = FALSE; 398 } 399 if (IsModified != NULL) { 400 *IsModified = FALSE; 401 } 402 403 // 404 // Below logic is to check 2M/4K page to make sure we donot waist memory. 405 // 406 while (Length != 0) { 407 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute); 408 if (PageEntry == NULL) { 409 return RETURN_UNSUPPORTED; 410 } 411 PageEntryLength = PageAttributeToLength (PageAttribute); 412 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute); 413 if (SplitAttribute == PageNone) { 414 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified); 415 if (IsEntryModified) { 416 if (IsModified != NULL) { 417 *IsModified = TRUE; 418 } 419 } 420 // 421 // Convert success, move to next 422 // 423 BaseAddress += PageEntryLength; 424 Length -= PageEntryLength; 425 } else { 426 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute); 427 if (RETURN_ERROR (Status)) { 428 return RETURN_UNSUPPORTED; 429 } 430 if (IsSplitted != NULL) { 431 *IsSplitted = TRUE; 432 } 433 if (IsModified != NULL) { 434 *IsModified = TRUE; 435 } 436 // 437 // Just split current page 438 // Convert success in next around 439 // 440 } 441 } 442 443 return RETURN_SUCCESS; 444 } 445 446 /** 447 FlushTlb on current processor. 448 449 @param[in,out] Buffer Pointer to private data buffer. 450 **/ 451 VOID 452 EFIAPI 453 FlushTlbOnCurrentProcessor ( 454 IN OUT VOID *Buffer 455 ) 456 { 457 CpuFlushTlb (); 458 } 459 460 /** 461 FlushTlb for all processors. 462 **/ 463 VOID 464 FlushTlbForAll ( 465 VOID 466 ) 467 { 468 UINTN Index; 469 470 FlushTlbOnCurrentProcessor (NULL); 471 472 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { 473 if (Index != gSmst->CurrentlyExecutingCpu) { 474 // Force to start up AP in blocking mode, 475 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL); 476 // Do not check return status, because AP might not be present in some corner cases. 477 } 478 } 479 } 480 481 /** 482 This function sets the attributes for the memory region specified by BaseAddress and 483 Length from their current attributes to the attributes specified by Attributes. 484 485 @param[in] BaseAddress The physical address that is the start address of a memory region. 486 @param[in] Length The size in bytes of the memory region. 487 @param[in] Attributes The bit mask of attributes to set for the memory region. 488 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. 489 490 @retval EFI_SUCCESS The attributes were set for the memory region. 491 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by 492 BaseAddress and Length cannot be modified. 493 @retval EFI_INVALID_PARAMETER Length is zero. 494 Attributes specified an illegal combination of attributes that 495 cannot be set together. 496 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of 497 the memory resource range. 498 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory 499 resource range specified by BaseAddress and Length. 500 The bit mask of attributes is not support for the memory resource 501 range specified by BaseAddress and Length. 502 503 **/ 504 EFI_STATUS 505 EFIAPI 506 SmmSetMemoryAttributesEx ( 507 IN EFI_PHYSICAL_ADDRESS BaseAddress, 508 IN UINT64 Length, 509 IN UINT64 Attributes, 510 OUT BOOLEAN *IsSplitted OPTIONAL 511 ) 512 { 513 EFI_STATUS Status; 514 BOOLEAN IsModified; 515 516 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified); 517 if (!EFI_ERROR(Status)) { 518 if (IsModified) { 519 // 520 // Flush TLB as last step 521 // 522 FlushTlbForAll(); 523 } 524 } 525 526 return Status; 527 } 528 529 /** 530 This function clears the attributes for the memory region specified by BaseAddress and 531 Length from their current attributes to the attributes specified by Attributes. 532 533 @param[in] BaseAddress The physical address that is the start address of a memory region. 534 @param[in] Length The size in bytes of the memory region. 535 @param[in] Attributes The bit mask of attributes to clear for the memory region. 536 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. 537 538 @retval EFI_SUCCESS The attributes were cleared for the memory region. 539 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by 540 BaseAddress and Length cannot be modified. 541 @retval EFI_INVALID_PARAMETER Length is zero. 542 Attributes specified an illegal combination of attributes that 543 cannot be set together. 544 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of 545 the memory resource range. 546 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory 547 resource range specified by BaseAddress and Length. 548 The bit mask of attributes is not support for the memory resource 549 range specified by BaseAddress and Length. 550 551 **/ 552 EFI_STATUS 553 EFIAPI 554 SmmClearMemoryAttributesEx ( 555 IN EFI_PHYSICAL_ADDRESS BaseAddress, 556 IN UINT64 Length, 557 IN UINT64 Attributes, 558 OUT BOOLEAN *IsSplitted OPTIONAL 559 ) 560 { 561 EFI_STATUS Status; 562 BOOLEAN IsModified; 563 564 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified); 565 if (!EFI_ERROR(Status)) { 566 if (IsModified) { 567 // 568 // Flush TLB as last step 569 // 570 FlushTlbForAll(); 571 } 572 } 573 574 return Status; 575 } 576 577 /** 578 This function sets the attributes for the memory region specified by BaseAddress and 579 Length from their current attributes to the attributes specified by Attributes. 580 581 @param[in] BaseAddress The physical address that is the start address of a memory region. 582 @param[in] Length The size in bytes of the memory region. 583 @param[in] Attributes The bit mask of attributes to set for the memory region. 584 585 @retval EFI_SUCCESS The attributes were set for the memory region. 586 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by 587 BaseAddress and Length cannot be modified. 588 @retval EFI_INVALID_PARAMETER Length is zero. 589 Attributes specified an illegal combination of attributes that 590 cannot be set together. 591 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of 592 the memory resource range. 593 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory 594 resource range specified by BaseAddress and Length. 595 The bit mask of attributes is not support for the memory resource 596 range specified by BaseAddress and Length. 597 598 **/ 599 EFI_STATUS 600 EFIAPI 601 SmmSetMemoryAttributes ( 602 IN EFI_PHYSICAL_ADDRESS BaseAddress, 603 IN UINT64 Length, 604 IN UINT64 Attributes 605 ) 606 { 607 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL); 608 } 609 610 /** 611 This function clears the attributes for the memory region specified by BaseAddress and 612 Length from their current attributes to the attributes specified by Attributes. 613 614 @param[in] BaseAddress The physical address that is the start address of a memory region. 615 @param[in] Length The size in bytes of the memory region. 616 @param[in] Attributes The bit mask of attributes to clear for the memory region. 617 618 @retval EFI_SUCCESS The attributes were cleared for the memory region. 619 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by 620 BaseAddress and Length cannot be modified. 621 @retval EFI_INVALID_PARAMETER Length is zero. 622 Attributes specified an illegal combination of attributes that 623 cannot be set together. 624 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of 625 the memory resource range. 626 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory 627 resource range specified by BaseAddress and Length. 628 The bit mask of attributes is not support for the memory resource 629 range specified by BaseAddress and Length. 630 631 **/ 632 EFI_STATUS 633 EFIAPI 634 SmmClearMemoryAttributes ( 635 IN EFI_PHYSICAL_ADDRESS BaseAddress, 636 IN UINT64 Length, 637 IN UINT64 Attributes 638 ) 639 { 640 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL); 641 } 642 643 644 645 /** 646 Retrieves a pointer to the system configuration table from the SMM System Table 647 based on a specified GUID. 648 649 @param[in] TableGuid The pointer to table's GUID type. 650 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table. 651 652 @retval EFI_SUCCESS A configuration table matching TableGuid was found. 653 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found. 654 655 **/ 656 EFI_STATUS 657 EFIAPI 658 SmmGetSystemConfigurationTable ( 659 IN EFI_GUID *TableGuid, 660 OUT VOID **Table 661 ) 662 { 663 UINTN Index; 664 665 ASSERT (TableGuid != NULL); 666 ASSERT (Table != NULL); 667 668 *Table = NULL; 669 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) { 670 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) { 671 *Table = gSmst->SmmConfigurationTable[Index].VendorTable; 672 return EFI_SUCCESS; 673 } 674 } 675 676 return EFI_NOT_FOUND; 677 } 678 679 /** 680 This function sets SMM save state buffer to be RW and XP. 681 **/ 682 VOID 683 PatchSmmSaveStateMap ( 684 VOID 685 ) 686 { 687 UINTN Index; 688 UINTN TileCodeSize; 689 UINTN TileDataSize; 690 UINTN TileSize; 691 692 TileCodeSize = GetSmiHandlerSize (); 693 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB); 694 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP); 695 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB); 696 TileSize = TileDataSize + TileCodeSize - 1; 697 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize); 698 699 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n")); 700 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) { 701 // 702 // Code 703 // 704 SmmSetMemoryAttributes ( 705 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET, 706 TileCodeSize, 707 EFI_MEMORY_RO 708 ); 709 SmmClearMemoryAttributes ( 710 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET, 711 TileCodeSize, 712 EFI_MEMORY_XP 713 ); 714 715 // 716 // Data 717 // 718 SmmClearMemoryAttributes ( 719 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize, 720 TileSize - TileCodeSize, 721 EFI_MEMORY_RO 722 ); 723 SmmSetMemoryAttributes ( 724 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize, 725 TileSize - TileCodeSize, 726 EFI_MEMORY_XP 727 ); 728 } 729 730 // 731 // Code 732 // 733 SmmSetMemoryAttributes ( 734 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET, 735 TileCodeSize, 736 EFI_MEMORY_RO 737 ); 738 SmmClearMemoryAttributes ( 739 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET, 740 TileCodeSize, 741 EFI_MEMORY_XP 742 ); 743 744 // 745 // Data 746 // 747 SmmClearMemoryAttributes ( 748 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize, 749 SIZE_32KB - TileCodeSize, 750 EFI_MEMORY_RO 751 ); 752 SmmSetMemoryAttributes ( 753 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize, 754 SIZE_32KB - TileCodeSize, 755 EFI_MEMORY_XP 756 ); 757 } 758 759 /** 760 This function sets memory attribute according to MemoryAttributesTable. 761 **/ 762 VOID 763 SetMemMapAttributes ( 764 VOID 765 ) 766 { 767 EFI_MEMORY_DESCRIPTOR *MemoryMap; 768 EFI_MEMORY_DESCRIPTOR *MemoryMapStart; 769 UINTN MemoryMapEntryCount; 770 UINTN DescriptorSize; 771 UINTN Index; 772 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; 773 774 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable); 775 if (MemoryAttributesTable == NULL) { 776 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n")); 777 return ; 778 } 779 780 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n")); 781 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version)); 782 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries)); 783 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize)); 784 785 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries; 786 DescriptorSize = MemoryAttributesTable->DescriptorSize; 787 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1); 788 MemoryMap = MemoryMapStart; 789 for (Index = 0; Index < MemoryMapEntryCount; Index++) { 790 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap)); 791 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type)); 792 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart)); 793 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart)); 794 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages)); 795 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute)); 796 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize); 797 } 798 799 MemoryMap = MemoryMapStart; 800 for (Index = 0; Index < MemoryMapEntryCount; Index++) { 801 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages)); 802 switch (MemoryMap->Type) { 803 case EfiRuntimeServicesCode: 804 SmmSetMemoryAttributes ( 805 MemoryMap->PhysicalStart, 806 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), 807 EFI_MEMORY_RO 808 ); 809 break; 810 case EfiRuntimeServicesData: 811 SmmSetMemoryAttributes ( 812 MemoryMap->PhysicalStart, 813 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), 814 EFI_MEMORY_XP 815 ); 816 break; 817 default: 818 SmmSetMemoryAttributes ( 819 MemoryMap->PhysicalStart, 820 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), 821 EFI_MEMORY_XP 822 ); 823 break; 824 } 825 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize); 826 } 827 828 PatchSmmSaveStateMap (); 829 PatchGdtIdtMap (); 830 831 return ; 832 } 833 834 /** 835 Sort memory map entries based upon PhysicalStart, from low to high. 836 837 @param MemoryMap A pointer to the buffer in which firmware places 838 the current memory map. 839 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer. 840 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. 841 **/ 842 STATIC 843 VOID 844 SortMemoryMap ( 845 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, 846 IN UINTN MemoryMapSize, 847 IN UINTN DescriptorSize 848 ) 849 { 850 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; 851 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; 852 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; 853 EFI_MEMORY_DESCRIPTOR TempMemoryMap; 854 855 MemoryMapEntry = MemoryMap; 856 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); 857 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); 858 while (MemoryMapEntry < MemoryMapEnd) { 859 while (NextMemoryMapEntry < MemoryMapEnd) { 860 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) { 861 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); 862 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); 863 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR)); 864 } 865 866 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); 867 } 868 869 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); 870 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); 871 } 872 } 873 874 /** 875 Return if a UEFI memory page should be marked as not present in SMM page table. 876 If the memory map entries type is 877 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, 878 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE. 879 Or return FALSE. 880 881 @param[in] MemoryMap A pointer to the memory descriptor. 882 883 @return TRUE The memory described will be marked as not present in SMM page table. 884 @return FALSE The memory described will not be marked as not present in SMM page table. 885 **/ 886 BOOLEAN 887 IsUefiPageNotPresent ( 888 IN EFI_MEMORY_DESCRIPTOR *MemoryMap 889 ) 890 { 891 switch (MemoryMap->Type) { 892 case EfiLoaderCode: 893 case EfiLoaderData: 894 case EfiBootServicesCode: 895 case EfiBootServicesData: 896 case EfiConventionalMemory: 897 case EfiUnusableMemory: 898 case EfiACPIReclaimMemory: 899 return TRUE; 900 default: 901 return FALSE; 902 } 903 } 904 905 /** 906 Merge continous memory map entries whose type is 907 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, 908 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by 909 these entries will be set as NOT present in SMM page table. 910 911 @param[in, out] MemoryMap A pointer to the buffer in which firmware places 912 the current memory map. 913 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the 914 MemoryMap buffer. On input, this is the size of 915 the current memory map. On output, 916 it is the size of new memory map after merge. 917 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. 918 **/ 919 STATIC 920 VOID 921 MergeMemoryMapForNotPresentEntry ( 922 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, 923 IN OUT UINTN *MemoryMapSize, 924 IN UINTN DescriptorSize 925 ) 926 { 927 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; 928 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; 929 UINT64 MemoryBlockLength; 930 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry; 931 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; 932 933 MemoryMapEntry = MemoryMap; 934 NewMemoryMapEntry = MemoryMap; 935 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize); 936 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { 937 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); 938 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); 939 940 do { 941 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages)); 942 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && 943 IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) && 944 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) { 945 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; 946 if (NewMemoryMapEntry != MemoryMapEntry) { 947 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; 948 } 949 950 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); 951 continue; 952 } else { 953 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); 954 break; 955 } 956 } while (TRUE); 957 958 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); 959 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize); 960 } 961 962 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap; 963 964 return ; 965 } 966 967 /** 968 This function caches the UEFI memory map information. 969 **/ 970 VOID 971 GetUefiMemoryMap ( 972 VOID 973 ) 974 { 975 EFI_STATUS Status; 976 UINTN MapKey; 977 UINT32 DescriptorVersion; 978 EFI_MEMORY_DESCRIPTOR *MemoryMap; 979 UINTN UefiMemoryMapSize; 980 981 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n")); 982 983 UefiMemoryMapSize = 0; 984 MemoryMap = NULL; 985 Status = gBS->GetMemoryMap ( 986 &UefiMemoryMapSize, 987 MemoryMap, 988 &MapKey, 989 &mUefiDescriptorSize, 990 &DescriptorVersion 991 ); 992 ASSERT (Status == EFI_BUFFER_TOO_SMALL); 993 994 do { 995 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap); 996 ASSERT (MemoryMap != NULL); 997 if (MemoryMap == NULL) { 998 return ; 999 } 1000 1001 Status = gBS->GetMemoryMap ( 1002 &UefiMemoryMapSize, 1003 MemoryMap, 1004 &MapKey, 1005 &mUefiDescriptorSize, 1006 &DescriptorVersion 1007 ); 1008 if (EFI_ERROR (Status)) { 1009 gBS->FreePool (MemoryMap); 1010 MemoryMap = NULL; 1011 } 1012 } while (Status == EFI_BUFFER_TOO_SMALL); 1013 1014 if (MemoryMap == NULL) { 1015 return ; 1016 } 1017 1018 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize); 1019 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize); 1020 1021 mUefiMemoryMapSize = UefiMemoryMapSize; 1022 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap); 1023 ASSERT (mUefiMemoryMap != NULL); 1024 1025 gBS->FreePool (MemoryMap); 1026 } 1027 1028 /** 1029 This function sets UEFI memory attribute according to UEFI memory map. 1030 1031 The normal memory region is marked as not present, such as 1032 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, 1033 EfiUnusableMemory, EfiACPIReclaimMemory. 1034 **/ 1035 VOID 1036 SetUefiMemMapAttributes ( 1037 VOID 1038 ) 1039 { 1040 EFI_MEMORY_DESCRIPTOR *MemoryMap; 1041 UINTN MemoryMapEntryCount; 1042 UINTN Index; 1043 1044 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n")); 1045 1046 if (mUefiMemoryMap == NULL) { 1047 DEBUG ((DEBUG_INFO, "UefiMemoryMap - NULL\n")); 1048 return ; 1049 } 1050 1051 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize; 1052 MemoryMap = mUefiMemoryMap; 1053 for (Index = 0; Index < MemoryMapEntryCount; Index++) { 1054 if (IsUefiPageNotPresent(MemoryMap)) { 1055 DEBUG ((DEBUG_INFO, "UefiMemory protection: 0x%lx - 0x%lx\n", MemoryMap->PhysicalStart, MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages))); 1056 SmmSetMemoryAttributes ( 1057 MemoryMap->PhysicalStart, 1058 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), 1059 EFI_MEMORY_RP 1060 ); 1061 } 1062 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize); 1063 } 1064 1065 // 1066 // Do free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress(). 1067 // 1068 } 1069 1070 /** 1071 Return if the Address is forbidden as SMM communication buffer. 1072 1073 @param[in] Address the address to be checked 1074 1075 @return TRUE The address is forbidden as SMM communication buffer. 1076 @return FALSE The address is allowed as SMM communication buffer. 1077 **/ 1078 BOOLEAN 1079 IsSmmCommBufferForbiddenAddress ( 1080 IN UINT64 Address 1081 ) 1082 { 1083 EFI_MEMORY_DESCRIPTOR *MemoryMap; 1084 UINTN MemoryMapEntryCount; 1085 UINTN Index; 1086 1087 if (mUefiMemoryMap == NULL) { 1088 return FALSE; 1089 } 1090 1091 MemoryMap = mUefiMemoryMap; 1092 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize; 1093 for (Index = 0; Index < MemoryMapEntryCount; Index++) { 1094 if (IsUefiPageNotPresent (MemoryMap)) { 1095 if ((Address >= MemoryMap->PhysicalStart) && 1096 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) { 1097 return TRUE; 1098 } 1099 } 1100 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize); 1101 } 1102 return FALSE; 1103 } 1104