1 /**@file 2 Memory Detection for Virtual Machines. 3 4 Copyright (c) 2006 - 2014, 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 36 #include "Platform.h" 37 #include "Cmos.h" 38 39 UINT8 mPhysMemAddressWidth; 40 41 UINT32 42 GetSystemMemorySizeBelow4gb ( 43 VOID 44 ) 45 { 46 UINT8 Cmos0x34; 47 UINT8 Cmos0x35; 48 49 // 50 // CMOS 0x34/0x35 specifies the system memory above 16 MB. 51 // * CMOS(0x35) is the high byte 52 // * CMOS(0x34) is the low byte 53 // * The size is specified in 64kb chunks 54 // * Since this is memory above 16MB, the 16MB must be added 55 // into the calculation to get the total memory size. 56 // 57 58 Cmos0x34 = (UINT8) CmosRead8 (0x34); 59 Cmos0x35 = (UINT8) CmosRead8 (0x35); 60 61 return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB); 62 } 63 64 65 STATIC 66 UINT64 67 GetSystemMemorySizeAbove4gb ( 68 ) 69 { 70 UINT32 Size; 71 UINTN CmosIndex; 72 73 // 74 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB. 75 // * CMOS(0x5d) is the most significant size byte 76 // * CMOS(0x5c) is the middle size byte 77 // * CMOS(0x5b) is the least significant size byte 78 // * The size is specified in 64kb chunks 79 // 80 81 Size = 0; 82 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) { 83 Size = (UINT32) (Size << 8) + (UINT32) CmosRead8 (CmosIndex); 84 } 85 86 return LShiftU64 (Size, 16); 87 } 88 89 90 /** 91 Initialize the mPhysMemAddressWidth variable, based on guest RAM size. 92 **/ 93 VOID 94 AddressWidthInitialization ( 95 VOID 96 ) 97 { 98 UINT64 FirstNonAddress; 99 100 // 101 // As guest-physical memory size grows, the permanent PEI RAM requirements 102 // are dominated by the identity-mapping page tables built by the DXE IPL. 103 // The DXL IPL keys off of the physical address bits advertized in the CPU 104 // HOB. To conserve memory, we calculate the minimum address width here. 105 // 106 FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb (); 107 mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress); 108 109 // 110 // If FirstNonAddress is not an integral power of two, then we need an 111 // additional bit. 112 // 113 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) { 114 ++mPhysMemAddressWidth; 115 } 116 117 // 118 // The minimum address width is 36 (covers up to and excluding 64 GB, which 119 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for 120 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We 121 // can simply assert that here, since 48 bits are good enough for 256 TB. 122 // 123 if (mPhysMemAddressWidth <= 36) { 124 mPhysMemAddressWidth = 36; 125 } 126 ASSERT (mPhysMemAddressWidth <= 48); 127 } 128 129 130 /** 131 Calculate the cap for the permanent PEI memory. 132 **/ 133 STATIC 134 UINT32 135 GetPeiMemoryCap ( 136 VOID 137 ) 138 { 139 BOOLEAN Page1GSupport; 140 UINT32 RegEax; 141 UINT32 RegEdx; 142 UINT32 Pml4Entries; 143 UINT32 PdpEntries; 144 UINTN TotalPages; 145 146 // 147 // If DXE is 32-bit, then just return the traditional 64 MB cap. 148 // 149 #ifdef MDE_CPU_IA32 150 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 151 return SIZE_64MB; 152 } 153 #endif 154 155 // 156 // Dependent on physical address width, PEI memory allocations can be 157 // dominated by the page tables built for 64-bit DXE. So we key the cap off 158 // of those. The code below is based on CreateIdentityMappingPageTables() in 159 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c". 160 // 161 Page1GSupport = FALSE; 162 if (PcdGetBool (PcdUse1GPageTable)) { 163 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 164 if (RegEax >= 0x80000001) { 165 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); 166 if ((RegEdx & BIT26) != 0) { 167 Page1GSupport = TRUE; 168 } 169 } 170 } 171 172 if (mPhysMemAddressWidth <= 39) { 173 Pml4Entries = 1; 174 PdpEntries = 1 << (mPhysMemAddressWidth - 30); 175 ASSERT (PdpEntries <= 0x200); 176 } else { 177 Pml4Entries = 1 << (mPhysMemAddressWidth - 39); 178 ASSERT (Pml4Entries <= 0x200); 179 PdpEntries = 512; 180 } 181 182 TotalPages = Page1GSupport ? Pml4Entries + 1 : 183 (PdpEntries + 1) * Pml4Entries + 1; 184 ASSERT (TotalPages <= 0x40201); 185 186 // 187 // Add 64 MB for miscellaneous allocations. Note that for 188 // mPhysMemAddressWidth values close to 36, the cap will actually be 189 // dominated by this increment. 190 // 191 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB); 192 } 193 194 195 /** 196 Publish PEI core memory 197 198 @return EFI_SUCCESS The PEIM initialized successfully. 199 200 **/ 201 EFI_STATUS 202 PublishPeiMemory ( 203 VOID 204 ) 205 { 206 EFI_STATUS Status; 207 EFI_PHYSICAL_ADDRESS MemoryBase; 208 UINT64 MemorySize; 209 UINT64 LowerMemorySize; 210 UINT32 PeiMemoryCap; 211 212 if (mBootMode == BOOT_ON_S3_RESUME) { 213 MemoryBase = PcdGet32 (PcdS3AcpiReservedMemoryBase); 214 MemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize); 215 } else { 216 LowerMemorySize = GetSystemMemorySizeBelow4gb (); 217 if (FeaturePcdGet (PcdSmmSmramRequire)) { 218 // 219 // TSEG is chipped from the end of low RAM 220 // 221 LowerMemorySize -= FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 222 } 223 224 PeiMemoryCap = GetPeiMemoryCap (); 225 DEBUG ((EFI_D_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n", 226 __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10)); 227 228 // 229 // Determine the range of memory to use during PEI 230 // 231 // Technically we could lay the permanent PEI RAM over SEC's temporary 232 // decompression and scratch buffer even if "secure S3" is needed, since 233 // their lifetimes don't overlap. However, PeiFvInitialization() will cover 234 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory 235 // allocation HOB, and other allocations served from the permanent PEI RAM 236 // shouldn't overlap with that HOB. 237 // 238 MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ? 239 PcdGet32 (PcdOvmfDecompressionScratchEnd) : 240 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize); 241 MemorySize = LowerMemorySize - MemoryBase; 242 if (MemorySize > PeiMemoryCap) { 243 MemoryBase = LowerMemorySize - PeiMemoryCap; 244 MemorySize = PeiMemoryCap; 245 } 246 } 247 248 // 249 // Publish this memory to the PEI Core 250 // 251 Status = PublishSystemMemory(MemoryBase, MemorySize); 252 ASSERT_EFI_ERROR (Status); 253 254 return Status; 255 } 256 257 258 /** 259 Peform Memory Detection for QEMU / KVM 260 261 **/ 262 STATIC 263 VOID 264 QemuInitializeRam ( 265 VOID 266 ) 267 { 268 UINT64 LowerMemorySize; 269 UINT64 UpperMemorySize; 270 MTRR_SETTINGS MtrrSettings; 271 EFI_STATUS Status; 272 273 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__)); 274 275 // 276 // Determine total memory size available 277 // 278 LowerMemorySize = GetSystemMemorySizeBelow4gb (); 279 UpperMemorySize = GetSystemMemorySizeAbove4gb (); 280 281 if (mBootMode != BOOT_ON_S3_RESUME) { 282 // 283 // Create memory HOBs 284 // 285 AddMemoryRangeHob (0, BASE_512KB + BASE_128KB); 286 287 if (FeaturePcdGet (PcdSmmSmramRequire)) { 288 UINT32 TsegSize; 289 290 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 291 AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize); 292 AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize, 293 TRUE); 294 } else { 295 AddMemoryRangeHob (BASE_1MB, LowerMemorySize); 296 } 297 298 if (UpperMemorySize != 0) { 299 AddUntestedMemoryBaseSizeHob (BASE_4GB, UpperMemorySize); 300 } 301 } 302 303 // 304 // We'd like to keep the following ranges uncached: 305 // - [640 KB, 1 MB) 306 // - [LowerMemorySize, 4 GB) 307 // 308 // Everything else should be WB. Unfortunately, programming the inverse (ie. 309 // keeping the default UC, and configuring the complement set of the above as 310 // WB) is not reliable in general, because the end of the upper RAM can have 311 // practically any alignment, and we may not have enough variable MTRRs to 312 // cover it exactly. 313 // 314 if (IsMtrrSupported ()) { 315 MtrrGetAllMtrrs (&MtrrSettings); 316 317 // 318 // MTRRs disabled, fixed MTRRs disabled, default type is uncached 319 // 320 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0); 321 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0); 322 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0); 323 324 // 325 // flip default type to writeback 326 // 327 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06); 328 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables); 329 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6; 330 MtrrSetAllMtrrs (&MtrrSettings); 331 332 // 333 // Set memory range from 640KB to 1MB to uncacheable 334 // 335 Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB, 336 BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable); 337 ASSERT_EFI_ERROR (Status); 338 339 // 340 // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as 341 // uncacheable 342 // 343 Status = MtrrSetMemoryAttribute (LowerMemorySize, 344 SIZE_4GB - LowerMemorySize, CacheUncacheable); 345 ASSERT_EFI_ERROR (Status); 346 } 347 } 348 349 /** 350 Publish system RAM and reserve memory regions 351 352 **/ 353 VOID 354 InitializeRamRegions ( 355 VOID 356 ) 357 { 358 if (!mXen) { 359 QemuInitializeRam (); 360 } else { 361 XenPublishRamRegions (); 362 } 363 364 if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) { 365 // 366 // This is the memory range that will be used for PEI on S3 resume 367 // 368 BuildMemoryAllocationHob ( 369 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdS3AcpiReservedMemoryBase), 370 (UINT64)(UINTN) PcdGet32 (PcdS3AcpiReservedMemorySize), 371 EfiACPIMemoryNVS 372 ); 373 374 // 375 // Cover the initial RAM area used as stack and temporary PEI heap. 376 // 377 // This is reserved as ACPI NVS so it can be used on S3 resume. 378 // 379 BuildMemoryAllocationHob ( 380 PcdGet32 (PcdOvmfSecPeiTempRamBase), 381 PcdGet32 (PcdOvmfSecPeiTempRamSize), 382 EfiACPIMemoryNVS 383 ); 384 385 // 386 // SEC stores its table of GUIDed section handlers here. 387 // 388 BuildMemoryAllocationHob ( 389 PcdGet64 (PcdGuidedExtractHandlerTableAddress), 390 PcdGet32 (PcdGuidedExtractHandlerTableSize), 391 EfiACPIMemoryNVS 392 ); 393 394 #ifdef MDE_CPU_X64 395 // 396 // Reserve the initial page tables built by the reset vector code. 397 // 398 // Since this memory range will be used by the Reset Vector on S3 399 // resume, it must be reserved as ACPI NVS. 400 // 401 BuildMemoryAllocationHob ( 402 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase), 403 (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize), 404 EfiACPIMemoryNVS 405 ); 406 #endif 407 } 408 409 if (mBootMode != BOOT_ON_S3_RESUME) { 410 if (!FeaturePcdGet (PcdSmmSmramRequire)) { 411 // 412 // Reserve the lock box storage area 413 // 414 // Since this memory range will be used on S3 resume, it must be 415 // reserved as ACPI NVS. 416 // 417 // If S3 is unsupported, then various drivers might still write to the 418 // LockBox area. We ought to prevent DXE from serving allocation requests 419 // such that they would overlap the LockBox storage. 420 // 421 ZeroMem ( 422 (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase), 423 (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize) 424 ); 425 BuildMemoryAllocationHob ( 426 (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase), 427 (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize), 428 mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData 429 ); 430 } 431 432 if (FeaturePcdGet (PcdSmmSmramRequire)) { 433 UINT32 TsegSize; 434 435 // 436 // Make sure the TSEG area that we reported as a reserved memory resource 437 // cannot be used for reserved memory allocations. 438 // 439 TsegSize = FixedPcdGet8 (PcdQ35TsegMbytes) * SIZE_1MB; 440 BuildMemoryAllocationHob ( 441 GetSystemMemorySizeBelow4gb() - TsegSize, 442 TsegSize, 443 EfiReservedMemoryType 444 ); 445 } 446 } 447 } 448