2 Memory Detection for Virtual Machines.
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
14 // The package level header files this module uses
16 #include <IndustryStandard/E820.h>
17 #include <IndustryStandard/I440FxPiix4.h>
18 #include <IndustryStandard/Q35MchIch9.h>
19 #include <IndustryStandard/CloudHv.h>
20 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
25 // The Library classes this module consumes
27 #include <Library/BaseLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/HobLib.h>
31 #include <Library/IoLib.h>
32 #include <Library/MemEncryptSevLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/PciLib.h>
35 #include <Library/PeimEntryPoint.h>
36 #include <Library/ResourcePublicationLib.h>
37 #include <Library/MtrrLib.h>
38 #include <Library/QemuFwCfgLib.h>
39 #include <Library/QemuFwCfgSimpleParserLib.h>
44 Q35TsegMbytesInitialization (
48 UINT16 ExtendedTsegMbytes
;
49 RETURN_STATUS PcdStatus
;
51 ASSERT (mPlatformInfoHob
.HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
54 // Check if QEMU offers an extended TSEG.
56 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
57 // register, and reading back the register.
59 // On a QEMU machine type that does not offer an extended TSEG, the initial
60 // write overwrites whatever value a malicious guest OS may have placed in
61 // the (unimplemented) register, before entering S3 or rebooting.
62 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
64 // On a QEMU machine type that offers an extended TSEG, the initial write
65 // triggers an update to the register. Subsequently, the value read back
66 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
67 // number of megabytes.
69 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
), MCH_EXT_TSEG_MB_QUERY
);
70 ExtendedTsegMbytes
= PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
));
71 if (ExtendedTsegMbytes
== MCH_EXT_TSEG_MB_QUERY
) {
72 mPlatformInfoHob
.Q35TsegMbytes
= PcdGet16 (PcdQ35TsegMbytes
);
78 "%a: QEMU offers an extended TSEG (%d MB)\n",
82 PcdStatus
= PcdSet16S (PcdQ35TsegMbytes
, ExtendedTsegMbytes
);
83 ASSERT_RETURN_ERROR (PcdStatus
);
84 mPlatformInfoHob
.Q35TsegMbytes
= ExtendedTsegMbytes
;
88 Q35SmramAtDefaultSmbaseInitialization (
92 RETURN_STATUS PcdStatus
;
94 ASSERT (mPlatformInfoHob
.HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
);
96 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
= FALSE
;
97 if (FeaturePcdGet (PcdCsmEnable
)) {
100 "%a: SMRAM at default SMBASE not checked due to CSM\n",
107 CtlReg
= DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL
);
108 PciWrite8 (CtlReg
, MCH_DEFAULT_SMBASE_QUERY
);
109 CtlRegVal
= PciRead8 (CtlReg
);
110 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
= (BOOLEAN
)(CtlRegVal
==
111 MCH_DEFAULT_SMBASE_IN_RAM
);
114 "%a: SMRAM at default SMBASE %a\n",
116 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
? "found" : "not found"
120 PcdStatus
= PcdSetBoolS (
121 PcdQ35SmramAtDefaultSmbase
,
122 mPlatformInfoHob
.Q35SmramAtDefaultSmbase
124 ASSERT_RETURN_ERROR (PcdStatus
);
129 PlatformQemuUc32BaseInitialization (
130 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
133 UINT32 LowerMemorySize
;
135 if (PlatformInfoHob
->HostBridgeDevId
== 0xffff /* microvm */) {
139 if (PlatformInfoHob
->HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
) {
141 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
142 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
143 // setting PcdPciExpressBaseAddress such that describing the
144 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
145 // variable MTRRs (preferably 1 or 2).
147 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress
) <= MAX_UINT32
);
148 PlatformInfoHob
->Uc32Base
= (UINT32
)FixedPcdGet64 (PcdPciExpressBaseAddress
);
152 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
153 PlatformInfoHob
->Uc32Size
= CLOUDHV_MMIO_HOLE_SIZE
;
154 PlatformInfoHob
->Uc32Base
= CLOUDHV_MMIO_HOLE_ADDRESS
;
158 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_82441_DEVICE_ID
);
160 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
161 // variable MTRR suffices by truncating the size to a whole power of two,
162 // while keeping the end affixed to 4GB. This will round the base up.
164 LowerMemorySize
= PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
165 PlatformInfoHob
->Uc32Size
= GetPowerOfTwo32 ((UINT32
)(SIZE_4GB
- LowerMemorySize
));
166 PlatformInfoHob
->Uc32Base
= (UINT32
)(SIZE_4GB
- PlatformInfoHob
->Uc32Size
);
168 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
169 // Therefore mQemuUc32Base is at least 2GB.
171 ASSERT (PlatformInfoHob
->Uc32Base
>= BASE_2GB
);
173 if (PlatformInfoHob
->Uc32Base
!= LowerMemorySize
) {
176 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
177 "an UC32 size of 0x%x\n",
180 PlatformInfoHob
->Uc32Base
,
181 PlatformInfoHob
->Uc32Size
187 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
188 of the 32-bit address range.
190 Find the highest exclusive >=4GB RAM address, or produce memory resource
191 descriptor HOBs for RAM entries that start at or above 4GB.
193 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()
194 produces memory resource descriptor HOBs for RAM
195 entries that start at or above 4GB.
197 Otherwise, MaxAddress holds the highest exclusive
198 >=4GB RAM address on output. If QEMU's fw_cfg E820
199 RAM map contains no RAM entry that starts outside of
200 the 32-bit address range, then MaxAddress is exactly
203 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
205 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
206 whole multiple of sizeof(EFI_E820_ENTRY64). No
207 RAM entry was processed.
209 @return Error codes from QemuFwCfgFindFile(). No RAM
214 PlatformScanOrAdd64BitE820Ram (
215 IN BOOLEAN AddHighHob
,
216 OUT UINT64
*LowMemory OPTIONAL
,
217 OUT UINT64
*MaxAddress OPTIONAL
221 FIRMWARE_CONFIG_ITEM FwCfgItem
;
223 EFI_E820_ENTRY64 E820Entry
;
226 Status
= QemuFwCfgFindFile ("etc/e820", &FwCfgItem
, &FwCfgSize
);
227 if (EFI_ERROR (Status
)) {
231 if (FwCfgSize
% sizeof E820Entry
!= 0) {
232 return EFI_PROTOCOL_ERROR
;
235 if (LowMemory
!= NULL
) {
239 if (MaxAddress
!= NULL
) {
240 *MaxAddress
= BASE_4GB
;
243 QemuFwCfgSelectItem (FwCfgItem
);
244 for (Processed
= 0; Processed
< FwCfgSize
; Processed
+= sizeof E820Entry
) {
245 QemuFwCfgReadBytes (sizeof E820Entry
, &E820Entry
);
248 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
254 if (E820Entry
.Type
== EfiAcpiAddressRangeMemory
) {
255 if (AddHighHob
&& (E820Entry
.BaseAddr
>= BASE_4GB
)) {
260 // Round up the start address, and round down the end address.
262 Base
= ALIGN_VALUE (E820Entry
.BaseAddr
, (UINT64
)EFI_PAGE_SIZE
);
263 End
= (E820Entry
.BaseAddr
+ E820Entry
.Length
) &
264 ~(UINT64
)EFI_PAGE_MASK
;
266 PlatformAddMemoryRangeHob (Base
, End
);
269 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
277 if (MaxAddress
|| LowMemory
) {
280 Candidate
= E820Entry
.BaseAddr
+ E820Entry
.Length
;
281 if (MaxAddress
&& (Candidate
> *MaxAddress
)) {
282 *MaxAddress
= Candidate
;
285 "%a: MaxAddress=0x%Lx\n",
291 if (LowMemory
&& (Candidate
> *LowMemory
) && (Candidate
< BASE_4GB
)) {
292 *LowMemory
= Candidate
;
295 "%a: LowMemory=0x%Lx\n",
310 @param Entries Pointer to PVH memmap
311 @param Count Number of entries
316 GetPvhMemmapEntries (
317 struct hvm_memmap_table_entry
**Entries
,
321 UINT32
*PVHResetVectorData
;
322 struct hvm_start_info
*pvh_start_info
;
324 PVHResetVectorData
= (VOID
*)(UINTN
)PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
325 if (PVHResetVectorData
== 0) {
326 return EFI_NOT_FOUND
;
329 pvh_start_info
= (struct hvm_start_info
*)(UINTN
)PVHResetVectorData
[0];
331 *Entries
= (struct hvm_memmap_table_entry
*)(UINTN
)pvh_start_info
->memmap_paddr
;
332 *Count
= pvh_start_info
->memmap_entries
;
339 GetHighestSystemMemoryAddressFromPvhMemmap (
343 struct hvm_memmap_table_entry
*Memmap
;
344 UINT32 MemmapEntriesCount
;
345 struct hvm_memmap_table_entry
*Entry
;
348 UINT64 HighestAddress
;
353 Status
= GetPvhMemmapEntries (&Memmap
, &MemmapEntriesCount
);
354 ASSERT_EFI_ERROR (Status
);
356 for (Loop
= 0; Loop
< MemmapEntriesCount
; Loop
++) {
357 Entry
= Memmap
+ Loop
;
358 EntryEnd
= Entry
->addr
+ Entry
->size
;
360 if ((Entry
->type
== XEN_HVM_MEMMAP_TYPE_RAM
) &&
361 (EntryEnd
> HighestAddress
))
363 if (Below4gb
&& (EntryEnd
<= BASE_4GB
)) {
364 HighestAddress
= EntryEnd
;
365 } else if (!Below4gb
&& (EntryEnd
>= BASE_4GB
)) {
366 HighestAddress
= EntryEnd
;
371 return HighestAddress
;
376 PlatformGetSystemMemorySizeBelow4gb (
377 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
381 UINT64 LowerMemorySize
= 0;
385 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
386 // Get the information from PVH memmap
387 return (UINT32
)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE
);
390 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, &LowerMemorySize
, NULL
);
391 if ((Status
== EFI_SUCCESS
) && (LowerMemorySize
> 0)) {
392 return (UINT32
)LowerMemorySize
;
396 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
397 // * CMOS(0x35) is the high byte
398 // * CMOS(0x34) is the low byte
399 // * The size is specified in 64kb chunks
400 // * Since this is memory above 16MB, the 16MB must be added
401 // into the calculation to get the total memory size.
404 Cmos0x34
= (UINT8
)PlatformCmosRead8 (0x34);
405 Cmos0x35
= (UINT8
)PlatformCmosRead8 (0x35);
407 return (UINT32
)(((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
412 PlatformGetSystemMemorySizeAbove4gb (
419 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
420 // * CMOS(0x5d) is the most significant size byte
421 // * CMOS(0x5c) is the middle size byte
422 // * CMOS(0x5b) is the least significant size byte
423 // * The size is specified in 64kb chunks
427 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
428 Size
= (UINT32
)(Size
<< 8) + (UINT32
)PlatformCmosRead8 (CmosIndex
);
431 return LShiftU64 (Size
, 16);
435 Return the highest address that DXE could possibly use, plus one.
439 PlatformGetFirstNonAddress (
440 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
443 UINT64 FirstNonAddress
;
444 UINT32 FwCfgPciMmio64Mb
;
446 FIRMWARE_CONFIG_ITEM FwCfgItem
;
448 UINT64 HotPlugMemoryEnd
;
451 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
456 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
457 // address from it. This can express an address >= 4GB+1TB.
459 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
460 // can only express a size smaller than 1TB), and add it to 4GB.
462 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, NULL
, &FirstNonAddress
);
463 if (EFI_ERROR (Status
)) {
464 FirstNonAddress
= BASE_4GB
+ PlatformGetSystemMemorySizeAbove4gb ();
468 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
469 // resources to 32-bit anyway. See DegradeResource() in
470 // "PciResourceSupport.c".
473 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
474 return FirstNonAddress
;
480 // See if the user specified the number of megabytes for the 64-bit PCI host
481 // aperture. Accept an aperture size up to 16TB.
483 // As signaled by the "X-" prefix, this knob is experimental, and might go
486 Status
= QemuFwCfgParseUint32 (
487 "opt/ovmf/X-PciMmio64Mb",
492 case EFI_UNSUPPORTED
:
496 if (FwCfgPciMmio64Mb
<= 0x1000000) {
497 PlatformInfoHob
->PcdPciMmio64Size
= LShiftU64 (FwCfgPciMmio64Mb
, 20);
507 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
513 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
514 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
517 "%a: disabling 64-bit PCI host aperture\n",
523 // There's nothing more to do; the amount of memory above 4GB fully
524 // determines the highest address plus one. The memory hotplug area (see
525 // below) plays no role for the firmware in this case.
527 return FirstNonAddress
;
531 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
532 // absolute, exclusive end address for the memory hotplug area. This area
533 // starts right at the end of the memory above 4GB. The 64-bit PCI host
534 // aperture must be placed above it.
536 Status
= QemuFwCfgFindFile (
537 "etc/reserved-memory-end",
541 if (!EFI_ERROR (Status
) && (FwCfgSize
== sizeof HotPlugMemoryEnd
)) {
542 QemuFwCfgSelectItem (FwCfgItem
);
543 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
546 "%a: HotPlugMemoryEnd=0x%Lx\n",
551 ASSERT (HotPlugMemoryEnd
>= FirstNonAddress
);
552 FirstNonAddress
= HotPlugMemoryEnd
;
556 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
557 // that the host can map it with 1GB hugepages. Follow suit.
559 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (FirstNonAddress
, (UINT64
)SIZE_1GB
);
560 PlatformInfoHob
->PcdPciMmio64Size
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Size
, (UINT64
)SIZE_1GB
);
563 // The 64-bit PCI host aperture should also be "naturally" aligned. The
564 // alignment is determined by rounding the size of the aperture down to the
565 // next smaller or equal power of two. That is, align the aperture by the
566 // largest BAR size that can fit into it.
568 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Base
, GetPowerOfTwo64 (PlatformInfoHob
->PcdPciMmio64Size
));
571 // The useful address space ends with the 64-bit PCI host aperture.
573 FirstNonAddress
= PlatformInfoHob
->PcdPciMmio64Base
+ PlatformInfoHob
->PcdPciMmio64Size
;
574 return FirstNonAddress
;
578 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
582 PlatformAddressWidthInitialization (
583 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
586 UINT64 FirstNonAddress
;
587 UINT8 PhysMemAddressWidth
;
590 // As guest-physical memory size grows, the permanent PEI RAM requirements
591 // are dominated by the identity-mapping page tables built by the DXE IPL.
592 // The DXL IPL keys off of the physical address bits advertized in the CPU
593 // HOB. To conserve memory, we calculate the minimum address width here.
595 FirstNonAddress
= PlatformGetFirstNonAddress (PlatformInfoHob
);
596 PhysMemAddressWidth
= (UINT8
)HighBitSet64 (FirstNonAddress
);
599 // If FirstNonAddress is not an integral power of two, then we need an
602 if ((FirstNonAddress
& (FirstNonAddress
- 1)) != 0) {
603 ++PhysMemAddressWidth
;
607 // The minimum address width is 36 (covers up to and excluding 64 GB, which
608 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
609 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
610 // can simply assert that here, since 48 bits are good enough for 256 TB.
612 if (PhysMemAddressWidth
<= 36) {
613 PhysMemAddressWidth
= 36;
616 ASSERT (PhysMemAddressWidth
<= 48);
618 PlatformInfoHob
->FirstNonAddress
= FirstNonAddress
;
619 PlatformInfoHob
->PhysMemAddressWidth
= PhysMemAddressWidth
;
623 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
626 AddressWidthInitialization (
627 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
630 RETURN_STATUS PcdStatus
;
632 PlatformAddressWidthInitialization (PlatformInfoHob
);
635 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
636 // resources to 32-bit anyway. See DegradeResource() in
637 // "PciResourceSupport.c".
640 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
646 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
647 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
650 "%a: disabling 64-bit PCI host aperture\n",
653 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
654 ASSERT_RETURN_ERROR (PcdStatus
);
660 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
662 // The core PciHostBridgeDxe driver will automatically add this range to
663 // the GCD memory space map through our PciHostBridgeLib instance; here we
664 // only need to set the PCDs.
666 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, PlatformInfoHob
->PcdPciMmio64Base
);
667 ASSERT_RETURN_ERROR (PcdStatus
);
668 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, PlatformInfoHob
->PcdPciMmio64Size
);
669 ASSERT_RETURN_ERROR (PcdStatus
);
673 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
675 PlatformInfoHob
->PcdPciMmio64Base
,
676 PlatformInfoHob
->PcdPciMmio64Size
682 Calculate the cap for the permanent PEI memory.
690 BOOLEAN Page1GSupport
;
698 // If DXE is 32-bit, then just return the traditional 64 MB cap.
701 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
708 // Dependent on physical address width, PEI memory allocations can be
709 // dominated by the page tables built for 64-bit DXE. So we key the cap off
710 // of those. The code below is based on CreateIdentityMappingPageTables() in
711 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
713 Page1GSupport
= FALSE
;
714 if (PcdGetBool (PcdUse1GPageTable
)) {
715 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
716 if (RegEax
>= 0x80000001) {
717 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
718 if ((RegEdx
& BIT26
) != 0) {
719 Page1GSupport
= TRUE
;
724 if (mPlatformInfoHob
.PhysMemAddressWidth
<= 39) {
726 PdpEntries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 30);
727 ASSERT (PdpEntries
<= 0x200);
729 Pml4Entries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 39);
730 ASSERT (Pml4Entries
<= 0x200);
734 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
735 (PdpEntries
+ 1) * Pml4Entries
+ 1;
736 ASSERT (TotalPages
<= 0x40201);
739 // Add 64 MB for miscellaneous allocations. Note that for
740 // PhysMemAddressWidth values close to 36, the cap will actually be
741 // dominated by this increment.
743 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
747 Publish PEI core memory
749 @return EFI_SUCCESS The PEIM initialized successfully.
758 EFI_PHYSICAL_ADDRESS MemoryBase
;
760 UINT32 LowerMemorySize
;
762 UINT32 S3AcpiReservedMemoryBase
;
763 UINT32 S3AcpiReservedMemorySize
;
765 LowerMemorySize
= PlatformGetSystemMemorySizeBelow4gb (&mPlatformInfoHob
);
766 if (mPlatformInfoHob
.SmmSmramRequire
) {
768 // TSEG is chipped from the end of low RAM
770 LowerMemorySize
-= mPlatformInfoHob
.Q35TsegMbytes
* SIZE_1MB
;
773 S3AcpiReservedMemoryBase
= 0;
774 S3AcpiReservedMemorySize
= 0;
777 // If S3 is supported, then the S3 permanent PEI memory is placed next,
778 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
779 // is an approximation.
781 if (mPlatformInfoHob
.S3Supported
) {
782 S3AcpiReservedMemorySize
= SIZE_512KB
+
783 mPlatformInfoHob
.PcdCpuMaxLogicalProcessorNumber
*
784 PcdGet32 (PcdCpuApStackSize
);
785 S3AcpiReservedMemoryBase
= LowerMemorySize
- S3AcpiReservedMemorySize
;
786 LowerMemorySize
= S3AcpiReservedMemoryBase
;
789 mPlatformInfoHob
.S3AcpiReservedMemoryBase
= S3AcpiReservedMemoryBase
;
790 mPlatformInfoHob
.S3AcpiReservedMemorySize
= S3AcpiReservedMemorySize
;
792 if (mPlatformInfoHob
.BootMode
== BOOT_ON_S3_RESUME
) {
793 MemoryBase
= S3AcpiReservedMemoryBase
;
794 MemorySize
= S3AcpiReservedMemorySize
;
796 PeiMemoryCap
= GetPeiMemoryCap ();
799 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
801 mPlatformInfoHob
.PhysMemAddressWidth
,
806 // Determine the range of memory to use during PEI
808 // Technically we could lay the permanent PEI RAM over SEC's temporary
809 // decompression and scratch buffer even if "secure S3" is needed, since
810 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
811 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
812 // allocation HOB, and other allocations served from the permanent PEI RAM
813 // shouldn't overlap with that HOB.
815 MemoryBase
= mPlatformInfoHob
.S3Supported
&& mPlatformInfoHob
.SmmSmramRequire
?
816 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
817 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
818 MemorySize
= LowerMemorySize
- MemoryBase
;
819 if (MemorySize
> PeiMemoryCap
) {
820 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
821 MemorySize
= PeiMemoryCap
;
826 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
827 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
828 // permanent PEI RAM is located even higher.
830 if (mPlatformInfoHob
.SmmSmramRequire
&& mPlatformInfoHob
.Q35SmramAtDefaultSmbase
) {
831 ASSERT (SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
<= MemoryBase
);
835 // Publish this memory to the PEI Core
837 Status
= PublishSystemMemory (MemoryBase
, MemorySize
);
838 ASSERT_EFI_ERROR (Status
);
845 QemuInitializeRamBelow1gb (
846 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
849 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
850 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE
);
851 PlatformAddReservedMemoryBaseSizeHob (
853 MCH_DEFAULT_SMBASE_SIZE
,
857 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
< BASE_512KB
+ BASE_128KB
,
858 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
860 PlatformAddMemoryRangeHob (
861 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
,
862 BASE_512KB
+ BASE_128KB
865 PlatformAddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
870 Peform Memory Detection for QEMU / KVM
875 PlatformQemuInitializeRam (
876 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
879 UINT64 LowerMemorySize
;
880 UINT64 UpperMemorySize
;
881 MTRR_SETTINGS MtrrSettings
;
884 DEBUG ((DEBUG_INFO
, "%a called\n", __FUNCTION__
));
887 // Determine total memory size available
889 LowerMemorySize
= PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
891 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
893 // Create the following memory HOB as an exception on the S3 boot path.
895 // Normally we'd create memory HOBs only on the normal boot path. However,
896 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
897 // well, for "borrowing" a subset of it temporarily, for the AP startup
900 // CpuMpPei saves the original contents of the borrowed area in permanent
901 // PEI RAM, in a backup buffer allocated with the normal PEI services.
902 // CpuMpPei restores the original contents ("returns" the borrowed area) at
903 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
904 // transferring control to the OS's wakeup vector in the FACS.
906 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
907 // restore the original contents. Furthermore, we expect all such PEIMs
908 // (CpuMpPei included) to claim the borrowed areas by producing memory
909 // allocation HOBs, and to honor preexistent memory allocation HOBs when
910 // looking for an area to borrow.
912 QemuInitializeRamBelow1gb (PlatformInfoHob
);
915 // Create memory HOBs
917 QemuInitializeRamBelow1gb (PlatformInfoHob
);
919 if (PlatformInfoHob
->SmmSmramRequire
) {
922 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
923 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
- TsegSize
);
924 PlatformAddReservedMemoryBaseSizeHob (
925 LowerMemorySize
- TsegSize
,
930 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
);
934 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
935 // entries. Otherwise, create a single memory HOB with the flat >=4GB
936 // memory size read from the CMOS.
938 Status
= PlatformScanOrAdd64BitE820Ram (TRUE
, NULL
, NULL
);
939 if (EFI_ERROR (Status
)) {
940 UpperMemorySize
= PlatformGetSystemMemorySizeAbove4gb ();
941 if (UpperMemorySize
!= 0) {
942 PlatformAddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
948 // We'd like to keep the following ranges uncached:
950 // - [LowerMemorySize, 4 GB)
952 // Everything else should be WB. Unfortunately, programming the inverse (ie.
953 // keeping the default UC, and configuring the complement set of the above as
954 // WB) is not reliable in general, because the end of the upper RAM can have
955 // practically any alignment, and we may not have enough variable MTRRs to
958 if (IsMtrrSupported () && (PlatformInfoHob
->HostBridgeDevId
!= CLOUDHV_DEVICE_ID
)) {
959 MtrrGetAllMtrrs (&MtrrSettings
);
962 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
964 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
965 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
966 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
969 // flip default type to writeback
971 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
972 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
973 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
974 MtrrSetAllMtrrs (&MtrrSettings
);
977 // Set memory range from 640KB to 1MB to uncacheable
979 Status
= MtrrSetMemoryAttribute (
980 BASE_512KB
+ BASE_128KB
,
981 BASE_1MB
- (BASE_512KB
+ BASE_128KB
),
984 ASSERT_EFI_ERROR (Status
);
987 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
988 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
990 Status
= MtrrSetMemoryAttribute (
991 PlatformInfoHob
->Uc32Base
,
992 SIZE_4GB
- PlatformInfoHob
->Uc32Base
,
995 ASSERT_EFI_ERROR (Status
);
1001 PlatformQemuInitializeRamForS3 (
1002 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1005 if (PlatformInfoHob
->S3Supported
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
1007 // This is the memory range that will be used for PEI on S3 resume
1009 BuildMemoryAllocationHob (
1010 PlatformInfoHob
->S3AcpiReservedMemoryBase
,
1011 PlatformInfoHob
->S3AcpiReservedMemorySize
,
1016 // Cover the initial RAM area used as stack and temporary PEI heap.
1018 // This is reserved as ACPI NVS so it can be used on S3 resume.
1020 BuildMemoryAllocationHob (
1021 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
1022 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
1027 // SEC stores its table of GUIDed section handlers here.
1029 BuildMemoryAllocationHob (
1030 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
1031 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
1037 // Reserve the initial page tables built by the reset vector code.
1039 // Since this memory range will be used by the Reset Vector on S3
1040 // resume, it must be reserved as ACPI NVS.
1042 BuildMemoryAllocationHob (
1043 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
),
1044 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesSize
),
1048 if (PlatformInfoHob
->SevEsIsEnabled
) {
1050 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1051 // includes the extra page table used to break down the 2MB page
1052 // mapping into 4KB page entries where the GHCB resides and the
1053 // GHCB area itself.
1055 // Since this memory range will be used by the Reset Vector on S3
1056 // resume, it must be reserved as ACPI NVS.
1058 BuildMemoryAllocationHob (
1059 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableBase
),
1060 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableSize
),
1063 BuildMemoryAllocationHob (
1064 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBase
),
1065 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbSize
),
1068 BuildMemoryAllocationHob (
1069 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupBase
),
1070 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupSize
),
1078 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
1079 if (!PlatformInfoHob
->SmmSmramRequire
) {
1081 // Reserve the lock box storage area
1083 // Since this memory range will be used on S3 resume, it must be
1084 // reserved as ACPI NVS.
1086 // If S3 is unsupported, then various drivers might still write to the
1087 // LockBox area. We ought to prevent DXE from serving allocation requests
1088 // such that they would overlap the LockBox storage.
1091 (VOID
*)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1092 (UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
)
1094 BuildMemoryAllocationHob (
1095 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1096 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
),
1097 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1101 if (PlatformInfoHob
->SmmSmramRequire
) {
1105 // Make sure the TSEG area that we reported as a reserved memory resource
1106 // cannot be used for reserved memory allocations.
1108 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
1109 BuildMemoryAllocationHob (
1110 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
) - TsegSize
,
1112 EfiReservedMemoryType
1115 // Similarly, allocate away the (already reserved) SMRAM at the default
1116 // SMBASE, if it exists.
1118 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
1119 BuildMemoryAllocationHob (
1121 MCH_DEFAULT_SMBASE_SIZE
,
1122 EfiReservedMemoryType
1128 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
1130 // Reserve the work area.
1132 // Since this memory range will be used by the Reset Vector on S3
1133 // resume, it must be reserved as ACPI NVS.
1135 // If S3 is unsupported, then various drivers might still write to the
1136 // work area. We ought to prevent DXE from serving allocation requests
1137 // such that they would overlap the work area.
1139 BuildMemoryAllocationHob (
1140 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
1141 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),
1142 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1151 Publish system RAM and reserve memory regions
1155 InitializeRamRegions (
1156 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1159 PlatformQemuInitializeRam (PlatformInfoHob
);
1161 SevInitializeRam ();
1163 PlatformQemuInitializeRamForS3 (PlatformInfoHob
);