1 /**@file 2 Memory Detection for Virtual Machines. 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 Module Name: 14 15 MemDetect.c 16 17 **/ 18 19 // 20 // The package level header files this module uses 21 // 22 #include <PiPei.h> 23 24 // 25 // The Library classes this module consumes 26 // 27 #include <Library/BaseMemoryLib.h> 28 #include <Library/DebugLib.h> 29 #include <Library/HobLib.h> 30 #include <Library/IoLib.h> 31 #include <Library/PcdLib.h> 32 #include <Library/PeimEntryPoint.h> 33 #include <Library/ResourcePublicationLib.h> 34 #include <Library/MtrrLib.h> 35 #include <Library/QemuFwCfgLib.h> 36 37 #include "Platform.h" 38 #include "Cmos.h" 39 40 UINT8 mPhysMemAddressWidth; 41 42 STATIC UINT32 mS3AcpiReservedMemoryBase; 43 STATIC UINT32 mS3AcpiReservedMemorySize; 44 45 UINT32 46 GetSystemMemorySizeBelow4gb ( 47 VOID 48 ) 49 { 50 UINT8 Cmos0x34; 51 UINT8 Cmos0x35; 52 53 // 54 // CMOS 0x34/0x35 specifies the system memory above 16 MB. 55 // * CMOS(0x35) is the high byte 56 // * CMOS(0x34) is the low byte 57 // * The size is specified in 64kb chunks 58 // * Since this is memory above 16MB, the 16MB must be added 59 // into the calculation to get the total memory size. 60 // 61 62 Cmos0x34 = (UINT8) CmosRead8 (0x34); 63 Cmos0x35 = (UINT8) CmosRead8 (0x35); 64 65 return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB); 66 } 67 68 69 STATIC 70 UINT64 71 GetSystemMemorySizeAbove4gb ( 72 ) 73 { 74 UINT32 Size; 75 UINTN CmosIndex; 76 77 // 78 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB. 79 // * CMOS(0x5d) is the most significant size byte 80 // * CMOS(0x5c) is the middle size byte 81 // * CMOS(0x5b) is the least significant size byte 82 // * The size is specified in 64kb chunks 83 // 84 85 Size = 0; 86 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) { 87 Size = (UINT32) (Size << 8) + (UINT32) CmosRead8 (CmosIndex); 88 } 89 90 return LShiftU64 (Size, 16); 91 } 92 93 94 /** 95 Return the highest address that DXE could possibly use, plus one. 96 **/ 97 STATIC 98 UINT64 99 GetFirstNonAddress ( 100 VOID 101 ) 102 { 103 UINT64 FirstNonAddress; 104 UINT64 Pci64Base, Pci64Size; 105 CHAR8 MbString[7 + 1]; 106 EFI_STATUS Status; 107 FIRMWARE_CONFIG_ITEM FwCfgItem; 108 UINTN FwCfgSize; 109 UINT64 HotPlugMemoryEnd; 110 RETURN_STATUS PcdStatus; 111 112 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb (); 113 114 // 115 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO 116 // resources to 32-bit anyway. See DegradeResource() in 117 // "PciResourceSupport.c". 118 // 119 #ifdef MDE_CPU_IA32 120 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 121 return FirstNonAddress; 122 } 123 #endif 124 125 // 126 // Otherwise, in order to calculate the highest address plus one, we must 127 // consider the 64-bit PCI host aperture too. Fetch the default size. 128 // 129 Pci64Size = PcdGet64 (PcdPciMmio64Size); 130 131 // 132 // See if the user specified the number of megabytes for the 64-bit PCI host 133 // aperture. The number of non-NUL characters in MbString allows for 134 // 9,999,999 MB, which is approximately 10 TB. 135 // 136 // As signaled by the "X-" prefix, this knob is experimental, and might go 137 // away at any time. 138 // 139 Status = QemuFwCfgFindFile ("opt/ovmf/X-PciMmio64Mb", &FwCfgItem, 140 &FwCfgSize); 141 if (!EFI_ERROR (Status)) { 142 if (FwCfgSize >= sizeof MbString) { 143 DEBUG ((EFI_D_WARN, 144 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n", 145 __FUNCTION__)); 146 } else { 147 QemuFwCfgSelectItem (FwCfgItem); 148 QemuFwCfgReadBytes (FwCfgSize, MbString); 149 MbString[FwCfgSize] = '\0'; 150 Pci64Size = LShiftU64 (AsciiStrDecimalToUint64 (MbString), 20); 151 } 152 } 153 154 if (Pci64Size == 0) { 155 if (mBootMode != BOOT_ON_S3_RESUME) { 156 DEBUG ((EFI_D_INFO, "%a: disabling 64-bit PCI host aperture\n", 157 __FUNCTION__)); 158 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0); 159 ASSERT_RETURN_ERROR (PcdStatus); 160 } 161 162 // 163 // There's nothing more to do; the amount of memory above 4GB fully 164 // determines the highest address plus one. The memory hotplug area (see 165 // below) plays no role for the firmware in this case. 166 // 167 return FirstNonAddress; 168 } 169 170 // 171 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an 172 // absolute, exclusive end address for the memory hotplug area. This area 173 // starts right at the end of the memory above 4GB. The 64-bit PCI host 174 // aperture must be placed above it. 175 // 176 Status = QemuFwCfgFindFile ("etc/reserved-memory-end", &FwCfgItem, 177 &FwCfgSize); 178 if (!EFI_ERROR (Status) && FwCfgSize == sizeof HotPlugMemoryEnd) { 179 QemuFwCfgSelectItem (FwCfgItem); 180 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd); 181 182 ASSERT (HotPlugMemoryEnd >= FirstNonAddress); 183 FirstNonAddress = HotPlugMemoryEnd; 184 } 185 186 // 187 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so 188 // that the host can map it with 1GB hugepages. Follow suit. 189 // 190 Pci64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB); 191 Pci64Size = ALIGN_VALUE (Pci64Size, (UINT64)SIZE_1GB); 192 193 // 194 // The 64-bit PCI host aperture should also be "naturally" aligned. The 195 // alignment is determined by rounding the size of the aperture down to the 196 // next smaller or equal power of two. That is, align the aperture by the 197 // largest BAR size that can fit into it. 198 // 199 Pci64Base = ALIGN_VALUE (Pci64Base, GetPowerOfTwo64 (Pci64Size)); 200 201 if (mBootMode != BOOT_ON_S3_RESUME) { 202 // 203 // The core PciHostBridgeDxe driver will automatically add this range to 204 // the GCD memory space map through our PciHostBridgeLib instance; here we 205 // only need to set the PCDs. 206 // 207 PcdStatus = PcdSet64S (PcdPciMmio64Base, Pci64Base); 208 ASSERT_RETURN_ERROR (PcdStatus); 209 PcdStatus = PcdSet64S (PcdPciMmio64Size, Pci64Size); 210 ASSERT_RETURN_ERROR (PcdStatus); 211 212 DEBUG ((EFI_D_INFO, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n", 213 __FUNCTION__, Pci64Base, Pci64Size)); 214 } 215 216 // 217 // The useful address space ends with the 64-bit PCI host aperture. 218 // 219 FirstNonAddress = Pci64Base + Pci64Size; 220 return FirstNonAddress; 221 } 222 223 224 /** 225 Initialize the mPhysMemAddressWidth variable, based on guest RAM size. 226 **/ 227 VOID 228 AddressWidthInitialization ( 229 VOID 230 ) 231 { 232 UINT64 FirstNonAddress; 233 234 // 235 // As guest-physical memory size grows, the permanent PEI RAM requirements 236 // are dominated by the identity-mapping page tables built by the DXE IPL. 237 // The DXL IPL keys off of the physical address bits advertized in the CPU 238 // HOB. To conserve memory, we calculate the minimum address width here. 239 // 240 FirstNonAddress = GetFirstNonAddress (); 241 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress); 242 243 // 244 // If FirstNonAddress is not an integral power of two, then we need an 245 // additional bit. 246 // 247 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) { 248 ++mPhysMemAddressWidth; 249 } 250 251 // 252 // The minimum address width is 36 (covers up to and excluding 64 GB, which 253 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for 254 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We 255 // can simply assert that here, since 48 bits are good enough for 256 TB. 256 // 257 if (mPhysMemAddressWidth <= 36) { 258 mPhysMemAddressWidth = 36; 259 } 260 ASSERT (mPhysMemAddressWidth <= 48); 261 } 262 263 264 /** 265 Calculate the cap for the permanent PEI memory. 266 **/ 267 STATIC 268 UINT32 269 GetPeiMemoryCap ( 270 VOID 271 ) 272 { 273 BOOLEAN Page1GSupport; 274 UINT32 RegEax; 275 UINT32 RegEdx; 276 UINT32 Pml4Entries; 277 UINT32 PdpEntries; 278 UINTN TotalPages; 279 280 // 281 // If DXE is 32-bit, then just return the traditional 64 MB cap. 282 // 283 #ifdef MDE_CPU_IA32 284 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 285 return SIZE_64MB; 286 } 287 #endif 288 289 // 290 // Dependent on physical address width, PEI memory allocations can be 291 // dominated by the page tables built for 64-bit DXE. So we key the cap off 292 // of those. The code below is based on CreateIdentityMappingPageTables() in 293 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c". 294 // 295 Page1GSupport = FALSE; 296 if (PcdGetBool (PcdUse1GPageTable)) { 297 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 298 if (RegEax >= 0x80000001) { 299 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); 300 if ((RegEdx & BIT26) != 0) { 301 Page1GSupport = TRUE; 302 } 303 } 304 } 305 306 if (mPhysMemAddressWidth <= 39) { 307 Pml4Entries = 1; 308 PdpEntries = 1 << (mPhysMemAddressWidth - 30); 309 ASSERT (PdpEntries <= 0x200); 310 } else { 311 Pml4Entries = 1 << (mPhysMemAddressWidth - 39); 312 ASSERT (Pml4Entries <= 0x200); 313 PdpEntries = 512; 314 } 315 316 TotalPages = Page1GSupport ? Pml4Entries + 1 : 317 (PdpEntries + 1) * Pml4Entries + 1; 318 ASSERT (TotalPages <= 0x40201); 319 320 // 321 // Add 64 MB for miscellaneous allocations. Note that for 322 // mPhysMemAddressWidth values close to 36, the cap will actually be 323 // dominated by this increment. 324 // 325 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB); 326 } 327 328 329 /** 330 Publish PEI core memory 331 332 @return EFI_SUCCESS The PEIM initialized successfully. 333 334 **/ 335 EFI_STATUS 336 PublishPeiMemory ( 337 VOID 338 ) 339 { 340 EFI_STATUS Status; 341 EFI_PHYSICAL_ADDRESS MemoryBase; 342 UINT64 MemorySize; 343 UINT32 LowerMemorySize; 344 UINT32 PeiMemoryCap; 345 346 LowerMemorySize = GetSystemMemorySizeBelow4gb (); 347 if (FeaturePcdGet (PcdSmmSmramRequire)) { 348 // 349 // TSEG is chipped from the end of low RAM 350 // 351 LowerMemorySize -= FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 352 } 353 354 // 355 // If S3 is supported, then the S3 permanent PEI memory is placed next, 356 // downwards. Its size is primarily dictated by CpuMpPei. The formula below 357 // is an approximation. 358 // 359 if (mS3Supported) { 360 mS3AcpiReservedMemorySize = SIZE_512KB + 361 mMaxCpuCount * 362 PcdGet32 (PcdCpuApStackSize); 363 mS3AcpiReservedMemoryBase = LowerMemorySize - mS3AcpiReservedMemorySize; 364 LowerMemorySize = mS3AcpiReservedMemoryBase; 365 } 366 367 if (mBootMode == BOOT_ON_S3_RESUME) { 368 MemoryBase = mS3AcpiReservedMemoryBase; 369 MemorySize = mS3AcpiReservedMemorySize; 370 } else { 371 PeiMemoryCap = GetPeiMemoryCap (); 372 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n", 373 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10)); 374 375 // 376 // Determine the range of memory to use during PEI 377 // 378 // Technically we could lay the permanent PEI RAM over SEC's temporary 379 // decompression and scratch buffer even if "secure S3" is needed, since 380 // their lifetimes don't overlap. However, PeiFvInitialization() will cover 381 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory 382 // allocation HOB, and other allocations served from the permanent PEI RAM 383 // shouldn't overlap with that HOB. 384 // 385 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ? 386 PcdGet32 (PcdOvmfDecompressionScratchEnd) : 387 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize); 388 MemorySize = LowerMemorySize - MemoryBase; 389 if (MemorySize > PeiMemoryCap) { 390 MemoryBase = LowerMemorySize - PeiMemoryCap; 391 MemorySize = PeiMemoryCap; 392 } 393 } 394 395 // 396 // Publish this memory to the PEI Core 397 // 398 Status = PublishSystemMemory(MemoryBase, MemorySize); 399 ASSERT_EFI_ERROR (Status); 400 401 return Status; 402 } 403 404 405 /** 406 Peform Memory Detection for QEMU / KVM 407 408 **/ 409 STATIC 410 VOID 411 QemuInitializeRam ( 412 VOID 413 ) 414 { 415 UINT64 LowerMemorySize; 416 UINT64 UpperMemorySize; 417 MTRR_SETTINGS MtrrSettings; 418 EFI_STATUS Status; 419 420 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__)); 421 422 // 423 // Determine total memory size available 424 // 425 LowerMemorySize = GetSystemMemorySizeBelow4gb (); 426 UpperMemorySize = GetSystemMemorySizeAbove4gb (); 427 428 if (mBootMode == BOOT_ON_S3_RESUME) { 429 // 430 // Create the following memory HOB as an exception on the S3 boot path. 431 // 432 // Normally we'd create memory HOBs only on the normal boot path. However, 433 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as 434 // well, for "borrowing" a subset of it temporarily, for the AP startup 435 // vector. 436 // 437 // CpuMpPei saves the original contents of the borrowed area in permanent 438 // PEI RAM, in a backup buffer allocated with the normal PEI services. 439 // CpuMpPei restores the original contents ("returns" the borrowed area) at 440 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before 441 // transferring control to the OS's wakeup vector in the FACS. 442 // 443 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to 444 // restore the original contents. Furthermore, we expect all such PEIMs 445 // (CpuMpPei included) to claim the borrowed areas by producing memory 446 // allocation HOBs, and to honor preexistent memory allocation HOBs when 447 // looking for an area to borrow. 448 // 449 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB); 450 } else { 451 // 452 // Create memory HOBs 453 // 454 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB); 455 456 if (FeaturePcdGet (PcdSmmSmramRequire)) { 457 UINT32 TsegSize; 458 459 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 460 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize); 461 AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize, 462 TRUE); 463 } else { 464 AddMemoryRangeHob (BASE_1MB, LowerMemorySize); 465 } 466 467 if (UpperMemorySize != 0) { 468 AddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize); 469 } 470 } 471 472 // 473 // We'd like to keep the following ranges uncached: 474 // - [640 KB, 1 MB) 475 // - [LowerMemorySize, 4 GB) 476 // 477 // Everything else should be WB. Unfortunately, programming the inverse (ie. 478 // keeping the default UC, and configuring the complement set of the above as 479 // WB) is not reliable in general, because the end of the upper RAM can have 480 // practically any alignment, and we may not have enough variable MTRRs to 481 // cover it exactly. 482 // 483 if (IsMtrrSupported ()) { 484 MtrrGetAllMtrrs (&MtrrSettings); 485 486 // 487 // MTRRs disabled, fixed MTRRs disabled, default type is uncached 488 // 489 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0); 490 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0); 491 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0); 492 493 // 494 // flip default type to writeback 495 // 496 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06); 497 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables); 498 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6; 499 MtrrSetAllMtrrs (&MtrrSettings); 500 501 // 502 // Set memory range from 640KB to 1MB to uncacheable 503 // 504 Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB, 505 BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable); 506 ASSERT_EFI_ERROR (Status); 507 508 // 509 // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as 510 // uncacheable 511 // 512 Status = MtrrSetMemoryAttribute (LowerMemorySize, 513 SIZE_4GB - LowerMemorySize, CacheUncacheable); 514 ASSERT_EFI_ERROR (Status); 515 } 516 } 517 518 /** 519 Publish system RAM and reserve memory regions 520 521 **/ 522 VOID 523 InitializeRamRegions ( 524 VOID 525 ) 526 { 527 if (!mXen) { 528 QemuInitializeRam (); 529 } else { 530 XenPublishRamRegions (); 531 } 532 533 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) { 534 // 535 // This is the memory range that will be used for PEI on S3 resume 536 // 537 BuildMemoryAllocationHob ( 538 mS3AcpiReservedMemoryBase, 539 mS3AcpiReservedMemorySize, 540 EfiACPIMemoryNVS 541 ); 542 543 // 544 // Cover the initial RAM area used as stack and temporary PEI heap. 545 // 546 // This is reserved as ACPI NVS so it can be used on S3 resume. 547 // 548 BuildMemoryAllocationHob ( 549 PcdGet32 (PcdOvmfSecPeiTempRamBase), 550 PcdGet32 (PcdOvmfSecPeiTempRamSize), 551 EfiACPIMemoryNVS 552 ); 553 554 // 555 // SEC stores its table of GUIDed section handlers here. 556 // 557 BuildMemoryAllocationHob ( 558 PcdGet64 (PcdGuidedExtractHandlerTableAddress), 559 PcdGet32 (PcdGuidedExtractHandlerTableSize), 560 EfiACPIMemoryNVS 561 ); 562 563 #ifdef MDE_CPU_X64 564 // 565 // Reserve the initial page tables built by the reset vector code. 566 // 567 // Since this memory range will be used by the Reset Vector on S3 568 // resume, it must be reserved as ACPI NVS. 569 // 570 BuildMemoryAllocationHob ( 571 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase), 572 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize), 573 EfiACPIMemoryNVS 574 ); 575 #endif 576 } 577 578 if (mBootMode != BOOT_ON_S3_RESUME) { 579 if (!FeaturePcdGet (PcdSmmSmramRequire)) { 580 // 581 // Reserve the lock box storage area 582 // 583 // Since this memory range will be used on S3 resume, it must be 584 // reserved as ACPI NVS. 585 // 586 // If S3 is unsupported, then various drivers might still write to the 587 // LockBox area. We ought to prevent DXE from serving allocation requests 588 // such that they would overlap the LockBox storage. 589 // 590 ZeroMem ( 591 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase), 592 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize) 593 ); 594 BuildMemoryAllocationHob ( 595 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase), 596 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize), 597 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData 598 ); 599 } 600 601 if (FeaturePcdGet (PcdSmmSmramRequire)) { 602 UINT32 TsegSize; 603 604 // 605 // Make sure the TSEG area that we reported as a reserved memory resource 606 // cannot be used for reserved memory allocations. 607 // 608 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 609 BuildMemoryAllocationHob ( 610 GetSystemMemorySizeBelow4gb() - TsegSize, 611 TsegSize, 612 EfiReservedMemoryType 613 ); 614 } 615 } 616 } 617