2 Memory Detection for Virtual Machines.
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
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.
20 // The package level header files this module uses
25 // The Library classes this module consumes
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>
40 UINT8 mPhysMemAddressWidth
;
42 STATIC UINT32 mS3AcpiReservedMemoryBase
;
43 STATIC UINT32 mS3AcpiReservedMemorySize
;
46 GetSystemMemorySizeBelow4gb (
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.
62 Cmos0x34
= (UINT8
) CmosRead8 (0x34);
63 Cmos0x35
= (UINT8
) CmosRead8 (0x35);
65 return (UINT32
) (((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
71 GetSystemMemorySizeAbove4gb (
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
86 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
87 Size
= (UINT32
) (Size
<< 8) + (UINT32
) CmosRead8 (CmosIndex
);
90 return LShiftU64 (Size
, 16);
95 Return the highest address that DXE could possibly use, plus one.
103 UINT64 FirstNonAddress
;
104 UINT64 Pci64Base
, Pci64Size
;
105 CHAR8 MbString
[7 + 1];
107 FIRMWARE_CONFIG_ITEM FwCfgItem
;
109 UINT64 HotPlugMemoryEnd
;
111 FirstNonAddress
= BASE_4GB
+ GetSystemMemorySizeAbove4gb ();
114 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
115 // resources to 32-bit anyway. See DegradeResource() in
116 // "PciResourceSupport.c".
119 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
120 return FirstNonAddress
;
125 // Otherwise, in order to calculate the highest address plus one, we must
126 // consider the 64-bit PCI host aperture too. Fetch the default size.
128 Pci64Size
= PcdGet64 (PcdPciMmio64Size
);
131 // See if the user specified the number of megabytes for the 64-bit PCI host
132 // aperture. The number of non-NUL characters in MbString allows for
133 // 9,999,999 MB, which is approximately 10 TB.
135 // As signaled by the "X-" prefix, this knob is experimental, and might go
138 Status
= QemuFwCfgFindFile ("opt/ovmf/X-PciMmio64Mb", &FwCfgItem
,
140 if (!EFI_ERROR (Status
)) {
141 if (FwCfgSize
>= sizeof MbString
) {
143 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
146 QemuFwCfgSelectItem (FwCfgItem
);
147 QemuFwCfgReadBytes (FwCfgSize
, MbString
);
148 MbString
[FwCfgSize
] = '\0';
149 Pci64Size
= LShiftU64 (AsciiStrDecimalToUint64 (MbString
), 20);
153 if (Pci64Size
== 0) {
154 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
155 DEBUG ((EFI_D_INFO
, "%a: disabling 64-bit PCI host aperture\n",
157 PcdSet64 (PcdPciMmio64Size
, 0);
161 // There's nothing more to do; the amount of memory above 4GB fully
162 // determines the highest address plus one. The memory hotplug area (see
163 // below) plays no role for the firmware in this case.
165 return FirstNonAddress
;
169 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
170 // absolute, exclusive end address for the memory hotplug area. This area
171 // starts right at the end of the memory above 4GB. The 64-bit PCI host
172 // aperture must be placed above it.
174 Status
= QemuFwCfgFindFile ("etc/reserved-memory-end", &FwCfgItem
,
176 if (!EFI_ERROR (Status
) && FwCfgSize
== sizeof HotPlugMemoryEnd
) {
177 QemuFwCfgSelectItem (FwCfgItem
);
178 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
180 ASSERT (HotPlugMemoryEnd
>= FirstNonAddress
);
181 FirstNonAddress
= HotPlugMemoryEnd
;
185 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
186 // that the host can map it with 1GB hugepages. Follow suit.
188 Pci64Base
= ALIGN_VALUE (FirstNonAddress
, (UINT64
)SIZE_1GB
);
189 Pci64Size
= ALIGN_VALUE (Pci64Size
, (UINT64
)SIZE_1GB
);
192 // The 64-bit PCI host aperture should also be "naturally" aligned. The
193 // alignment is determined by rounding the size of the aperture down to the
194 // next smaller or equal power of two. That is, align the aperture by the
195 // largest BAR size that can fit into it.
197 Pci64Base
= ALIGN_VALUE (Pci64Base
, GetPowerOfTwo64 (Pci64Size
));
199 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
201 // The core PciHostBridgeDxe driver will automatically add this range to
202 // the GCD memory space map through our PciHostBridgeLib instance; here we
203 // only need to set the PCDs.
205 PcdSet64 (PcdPciMmio64Base
, Pci64Base
);
206 PcdSet64 (PcdPciMmio64Size
, Pci64Size
);
207 DEBUG ((EFI_D_INFO
, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
208 __FUNCTION__
, Pci64Base
, Pci64Size
));
212 // The useful address space ends with the 64-bit PCI host aperture.
214 FirstNonAddress
= Pci64Base
+ Pci64Size
;
215 return FirstNonAddress
;
220 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
223 AddressWidthInitialization (
227 UINT64 FirstNonAddress
;
230 // As guest-physical memory size grows, the permanent PEI RAM requirements
231 // are dominated by the identity-mapping page tables built by the DXE IPL.
232 // The DXL IPL keys off of the physical address bits advertized in the CPU
233 // HOB. To conserve memory, we calculate the minimum address width here.
235 FirstNonAddress
= GetFirstNonAddress ();
236 mPhysMemAddressWidth
= (UINT8
)HighBitSet64 (FirstNonAddress
);
239 // If FirstNonAddress is not an integral power of two, then we need an
242 if ((FirstNonAddress
& (FirstNonAddress
- 1)) != 0) {
243 ++mPhysMemAddressWidth
;
247 // The minimum address width is 36 (covers up to and excluding 64 GB, which
248 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
249 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
250 // can simply assert that here, since 48 bits are good enough for 256 TB.
252 if (mPhysMemAddressWidth
<= 36) {
253 mPhysMemAddressWidth
= 36;
255 ASSERT (mPhysMemAddressWidth
<= 48);
260 Calculate the cap for the permanent PEI memory.
268 BOOLEAN Page1GSupport
;
276 // If DXE is 32-bit, then just return the traditional 64 MB cap.
279 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
285 // Dependent on physical address width, PEI memory allocations can be
286 // dominated by the page tables built for 64-bit DXE. So we key the cap off
287 // of those. The code below is based on CreateIdentityMappingPageTables() in
288 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
290 Page1GSupport
= FALSE
;
291 if (PcdGetBool (PcdUse1GPageTable
)) {
292 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
293 if (RegEax
>= 0x80000001) {
294 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
295 if ((RegEdx
& BIT26
) != 0) {
296 Page1GSupport
= TRUE
;
301 if (mPhysMemAddressWidth
<= 39) {
303 PdpEntries
= 1 << (mPhysMemAddressWidth
- 30);
304 ASSERT (PdpEntries
<= 0x200);
306 Pml4Entries
= 1 << (mPhysMemAddressWidth
- 39);
307 ASSERT (Pml4Entries
<= 0x200);
311 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
312 (PdpEntries
+ 1) * Pml4Entries
+ 1;
313 ASSERT (TotalPages
<= 0x40201);
316 // Add 64 MB for miscellaneous allocations. Note that for
317 // mPhysMemAddressWidth values close to 36, the cap will actually be
318 // dominated by this increment.
320 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
325 Publish PEI core memory
327 @return EFI_SUCCESS The PEIM initialized successfully.
336 EFI_PHYSICAL_ADDRESS MemoryBase
;
338 UINT32 LowerMemorySize
;
341 LowerMemorySize
= GetSystemMemorySizeBelow4gb ();
342 if (FeaturePcdGet (PcdSmmSmramRequire
)) {
344 // TSEG is chipped from the end of low RAM
346 LowerMemorySize
-= FixedPcdGet8 (PcdQ35TsegMbytes
) * SIZE_1MB
;
350 // If S3 is supported, then the S3 permanent PEI memory is placed next,
351 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
352 // is an approximation.
355 mS3AcpiReservedMemorySize
= SIZE_512KB
+
356 PcdGet32 (PcdCpuMaxLogicalProcessorNumber
) *
357 PcdGet32 (PcdCpuApStackSize
);
358 mS3AcpiReservedMemoryBase
= LowerMemorySize
- mS3AcpiReservedMemorySize
;
359 LowerMemorySize
= mS3AcpiReservedMemoryBase
;
362 if (mBootMode
== BOOT_ON_S3_RESUME
) {
363 MemoryBase
= mS3AcpiReservedMemoryBase
;
364 MemorySize
= mS3AcpiReservedMemorySize
;
366 PeiMemoryCap
= GetPeiMemoryCap ();
367 DEBUG ((EFI_D_INFO
, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
368 __FUNCTION__
, mPhysMemAddressWidth
, PeiMemoryCap
>> 10));
371 // Determine the range of memory to use during PEI
373 // Technically we could lay the permanent PEI RAM over SEC's temporary
374 // decompression and scratch buffer even if "secure S3" is needed, since
375 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
376 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
377 // allocation HOB, and other allocations served from the permanent PEI RAM
378 // shouldn't overlap with that HOB.
380 MemoryBase
= mS3Supported
&& FeaturePcdGet (PcdSmmSmramRequire
) ?
381 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
382 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
383 MemorySize
= LowerMemorySize
- MemoryBase
;
384 if (MemorySize
> PeiMemoryCap
) {
385 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
386 MemorySize
= PeiMemoryCap
;
391 // Publish this memory to the PEI Core
393 Status
= PublishSystemMemory(MemoryBase
, MemorySize
);
394 ASSERT_EFI_ERROR (Status
);
401 Peform Memory Detection for QEMU / KVM
410 UINT64 LowerMemorySize
;
411 UINT64 UpperMemorySize
;
412 MTRR_SETTINGS MtrrSettings
;
415 DEBUG ((EFI_D_INFO
, "%a called\n", __FUNCTION__
));
418 // Determine total memory size available
420 LowerMemorySize
= GetSystemMemorySizeBelow4gb ();
421 UpperMemorySize
= GetSystemMemorySizeAbove4gb ();
423 if (mBootMode
== BOOT_ON_S3_RESUME
) {
425 // Create the following memory HOB as an exception on the S3 boot path.
427 // Normally we'd create memory HOBs only on the normal boot path. However,
428 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
429 // well, for "borrowing" a subset of it temporarily, for the AP startup
432 // CpuMpPei saves the original contents of the borrowed area in permanent
433 // PEI RAM, in a backup buffer allocated with the normal PEI services.
434 // CpuMpPei restores the original contents ("returns" the borrowed area) at
435 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
436 // transfering control to the OS's wakeup vector in the FACS.
438 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
439 // restore the original contents. Furthermore, we expect all such PEIMs
440 // (CpuMpPei included) to claim the borrowed areas by producing memory
441 // allocation HOBs, and to honor preexistent memory allocation HOBs when
442 // looking for an area to borrow.
444 AddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
447 // Create memory HOBs
449 AddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
451 if (FeaturePcdGet (PcdSmmSmramRequire
)) {
454 TsegSize
= FixedPcdGet8 (PcdQ35TsegMbytes
) * SIZE_1MB
;
455 AddMemoryRangeHob (BASE_1MB
, LowerMemorySize
- TsegSize
);
456 AddReservedMemoryBaseSizeHob (LowerMemorySize
- TsegSize
, TsegSize
,
459 AddMemoryRangeHob (BASE_1MB
, LowerMemorySize
);
462 if (UpperMemorySize
!= 0) {
463 AddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
468 // We'd like to keep the following ranges uncached:
470 // - [LowerMemorySize, 4 GB)
472 // Everything else should be WB. Unfortunately, programming the inverse (ie.
473 // keeping the default UC, and configuring the complement set of the above as
474 // WB) is not reliable in general, because the end of the upper RAM can have
475 // practically any alignment, and we may not have enough variable MTRRs to
478 if (IsMtrrSupported ()) {
479 MtrrGetAllMtrrs (&MtrrSettings
);
482 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
484 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
485 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
486 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
489 // flip default type to writeback
491 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
492 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
493 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
494 MtrrSetAllMtrrs (&MtrrSettings
);
497 // Set memory range from 640KB to 1MB to uncacheable
499 Status
= MtrrSetMemoryAttribute (BASE_512KB
+ BASE_128KB
,
500 BASE_1MB
- (BASE_512KB
+ BASE_128KB
), CacheUncacheable
);
501 ASSERT_EFI_ERROR (Status
);
504 // Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as
507 Status
= MtrrSetMemoryAttribute (LowerMemorySize
,
508 SIZE_4GB
- LowerMemorySize
, CacheUncacheable
);
509 ASSERT_EFI_ERROR (Status
);
514 Publish system RAM and reserve memory regions
518 InitializeRamRegions (
523 QemuInitializeRam ();
525 XenPublishRamRegions ();
528 if (mS3Supported
&& mBootMode
!= BOOT_ON_S3_RESUME
) {
530 // This is the memory range that will be used for PEI on S3 resume
532 BuildMemoryAllocationHob (
533 mS3AcpiReservedMemoryBase
,
534 mS3AcpiReservedMemorySize
,
539 // Cover the initial RAM area used as stack and temporary PEI heap.
541 // This is reserved as ACPI NVS so it can be used on S3 resume.
543 BuildMemoryAllocationHob (
544 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
545 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
550 // SEC stores its table of GUIDed section handlers here.
552 BuildMemoryAllocationHob (
553 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
554 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
560 // Reserve the initial page tables built by the reset vector code.
562 // Since this memory range will be used by the Reset Vector on S3
563 // resume, it must be reserved as ACPI NVS.
565 BuildMemoryAllocationHob (
566 (EFI_PHYSICAL_ADDRESS
)(UINTN
) PcdGet32 (PcdOvmfSecPageTablesBase
),
567 (UINT64
)(UINTN
) PcdGet32 (PcdOvmfSecPageTablesSize
),
573 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
574 if (!FeaturePcdGet (PcdSmmSmramRequire
)) {
576 // Reserve the lock box storage area
578 // Since this memory range will be used on S3 resume, it must be
579 // reserved as ACPI NVS.
581 // If S3 is unsupported, then various drivers might still write to the
582 // LockBox area. We ought to prevent DXE from serving allocation requests
583 // such that they would overlap the LockBox storage.
586 (VOID
*)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageBase
),
587 (UINTN
) PcdGet32 (PcdOvmfLockBoxStorageSize
)
589 BuildMemoryAllocationHob (
590 (EFI_PHYSICAL_ADDRESS
)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageBase
),
591 (UINT64
)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageSize
),
592 mS3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
596 if (FeaturePcdGet (PcdSmmSmramRequire
)) {
600 // Make sure the TSEG area that we reported as a reserved memory resource
601 // cannot be used for reserved memory allocations.
603 TsegSize
= FixedPcdGet8 (PcdQ35TsegMbytes
) * SIZE_1MB
;
604 BuildMemoryAllocationHob (
605 GetSystemMemorySizeBelow4gb() - TsegSize
,
607 EfiReservedMemoryType