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
= GetSystemMemorySizeBelow4gb (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
;
375 GetSystemMemorySizeBelow4gb (
376 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
380 UINT64 LowerMemorySize
= 0;
384 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
385 // Get the information from PVH memmap
386 return (UINT32
)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE
);
389 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, &LowerMemorySize
, NULL
);
390 if ((Status
== EFI_SUCCESS
) && (LowerMemorySize
> 0)) {
391 return (UINT32
)LowerMemorySize
;
395 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
396 // * CMOS(0x35) is the high byte
397 // * CMOS(0x34) is the low byte
398 // * The size is specified in 64kb chunks
399 // * Since this is memory above 16MB, the 16MB must be added
400 // into the calculation to get the total memory size.
403 Cmos0x34
= (UINT8
)PlatformCmosRead8 (0x34);
404 Cmos0x35
= (UINT8
)PlatformCmosRead8 (0x35);
406 return (UINT32
)(((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
411 PlatformGetSystemMemorySizeAbove4gb (
418 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
419 // * CMOS(0x5d) is the most significant size byte
420 // * CMOS(0x5c) is the middle size byte
421 // * CMOS(0x5b) is the least significant size byte
422 // * The size is specified in 64kb chunks
426 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
427 Size
= (UINT32
)(Size
<< 8) + (UINT32
)PlatformCmosRead8 (CmosIndex
);
430 return LShiftU64 (Size
, 16);
434 Return the highest address that DXE could possibly use, plus one.
438 PlatformGetFirstNonAddress (
439 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
442 UINT64 FirstNonAddress
;
443 UINT32 FwCfgPciMmio64Mb
;
445 FIRMWARE_CONFIG_ITEM FwCfgItem
;
447 UINT64 HotPlugMemoryEnd
;
450 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
455 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
456 // address from it. This can express an address >= 4GB+1TB.
458 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
459 // can only express a size smaller than 1TB), and add it to 4GB.
461 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, NULL
, &FirstNonAddress
);
462 if (EFI_ERROR (Status
)) {
463 FirstNonAddress
= BASE_4GB
+ PlatformGetSystemMemorySizeAbove4gb ();
467 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
468 // resources to 32-bit anyway. See DegradeResource() in
469 // "PciResourceSupport.c".
472 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
473 return FirstNonAddress
;
479 // See if the user specified the number of megabytes for the 64-bit PCI host
480 // aperture. Accept an aperture size up to 16TB.
482 // As signaled by the "X-" prefix, this knob is experimental, and might go
485 Status
= QemuFwCfgParseUint32 (
486 "opt/ovmf/X-PciMmio64Mb",
491 case EFI_UNSUPPORTED
:
495 if (FwCfgPciMmio64Mb
<= 0x1000000) {
496 PlatformInfoHob
->PcdPciMmio64Size
= LShiftU64 (FwCfgPciMmio64Mb
, 20);
506 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
512 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
513 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
516 "%a: disabling 64-bit PCI host aperture\n",
522 // There's nothing more to do; the amount of memory above 4GB fully
523 // determines the highest address plus one. The memory hotplug area (see
524 // below) plays no role for the firmware in this case.
526 return FirstNonAddress
;
530 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
531 // absolute, exclusive end address for the memory hotplug area. This area
532 // starts right at the end of the memory above 4GB. The 64-bit PCI host
533 // aperture must be placed above it.
535 Status
= QemuFwCfgFindFile (
536 "etc/reserved-memory-end",
540 if (!EFI_ERROR (Status
) && (FwCfgSize
== sizeof HotPlugMemoryEnd
)) {
541 QemuFwCfgSelectItem (FwCfgItem
);
542 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
545 "%a: HotPlugMemoryEnd=0x%Lx\n",
550 ASSERT (HotPlugMemoryEnd
>= FirstNonAddress
);
551 FirstNonAddress
= HotPlugMemoryEnd
;
555 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
556 // that the host can map it with 1GB hugepages. Follow suit.
558 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (FirstNonAddress
, (UINT64
)SIZE_1GB
);
559 PlatformInfoHob
->PcdPciMmio64Size
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Size
, (UINT64
)SIZE_1GB
);
562 // The 64-bit PCI host aperture should also be "naturally" aligned. The
563 // alignment is determined by rounding the size of the aperture down to the
564 // next smaller or equal power of two. That is, align the aperture by the
565 // largest BAR size that can fit into it.
567 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Base
, GetPowerOfTwo64 (PlatformInfoHob
->PcdPciMmio64Size
));
570 // The useful address space ends with the 64-bit PCI host aperture.
572 FirstNonAddress
= PlatformInfoHob
->PcdPciMmio64Base
+ PlatformInfoHob
->PcdPciMmio64Size
;
573 return FirstNonAddress
;
577 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
581 PlatformAddressWidthInitialization (
582 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
585 UINT64 FirstNonAddress
;
586 UINT8 PhysMemAddressWidth
;
589 // As guest-physical memory size grows, the permanent PEI RAM requirements
590 // are dominated by the identity-mapping page tables built by the DXE IPL.
591 // The DXL IPL keys off of the physical address bits advertized in the CPU
592 // HOB. To conserve memory, we calculate the minimum address width here.
594 FirstNonAddress
= PlatformGetFirstNonAddress (PlatformInfoHob
);
595 PhysMemAddressWidth
= (UINT8
)HighBitSet64 (FirstNonAddress
);
598 // If FirstNonAddress is not an integral power of two, then we need an
601 if ((FirstNonAddress
& (FirstNonAddress
- 1)) != 0) {
602 ++PhysMemAddressWidth
;
606 // The minimum address width is 36 (covers up to and excluding 64 GB, which
607 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
608 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
609 // can simply assert that here, since 48 bits are good enough for 256 TB.
611 if (PhysMemAddressWidth
<= 36) {
612 PhysMemAddressWidth
= 36;
615 ASSERT (PhysMemAddressWidth
<= 48);
617 PlatformInfoHob
->FirstNonAddress
= FirstNonAddress
;
618 PlatformInfoHob
->PhysMemAddressWidth
= PhysMemAddressWidth
;
622 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
625 AddressWidthInitialization (
626 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
629 RETURN_STATUS PcdStatus
;
631 PlatformAddressWidthInitialization (PlatformInfoHob
);
634 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
635 // resources to 32-bit anyway. See DegradeResource() in
636 // "PciResourceSupport.c".
639 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
645 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
646 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
649 "%a: disabling 64-bit PCI host aperture\n",
652 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
653 ASSERT_RETURN_ERROR (PcdStatus
);
659 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
661 // The core PciHostBridgeDxe driver will automatically add this range to
662 // the GCD memory space map through our PciHostBridgeLib instance; here we
663 // only need to set the PCDs.
665 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, PlatformInfoHob
->PcdPciMmio64Base
);
666 ASSERT_RETURN_ERROR (PcdStatus
);
667 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, PlatformInfoHob
->PcdPciMmio64Size
);
668 ASSERT_RETURN_ERROR (PcdStatus
);
672 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
674 PlatformInfoHob
->PcdPciMmio64Base
,
675 PlatformInfoHob
->PcdPciMmio64Size
681 Calculate the cap for the permanent PEI memory.
689 BOOLEAN Page1GSupport
;
697 // If DXE is 32-bit, then just return the traditional 64 MB cap.
700 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
707 // Dependent on physical address width, PEI memory allocations can be
708 // dominated by the page tables built for 64-bit DXE. So we key the cap off
709 // of those. The code below is based on CreateIdentityMappingPageTables() in
710 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
712 Page1GSupport
= FALSE
;
713 if (PcdGetBool (PcdUse1GPageTable
)) {
714 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
715 if (RegEax
>= 0x80000001) {
716 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
717 if ((RegEdx
& BIT26
) != 0) {
718 Page1GSupport
= TRUE
;
723 if (mPlatformInfoHob
.PhysMemAddressWidth
<= 39) {
725 PdpEntries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 30);
726 ASSERT (PdpEntries
<= 0x200);
728 Pml4Entries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 39);
729 ASSERT (Pml4Entries
<= 0x200);
733 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
734 (PdpEntries
+ 1) * Pml4Entries
+ 1;
735 ASSERT (TotalPages
<= 0x40201);
738 // Add 64 MB for miscellaneous allocations. Note that for
739 // PhysMemAddressWidth values close to 36, the cap will actually be
740 // dominated by this increment.
742 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
746 Publish PEI core memory
748 @return EFI_SUCCESS The PEIM initialized successfully.
757 EFI_PHYSICAL_ADDRESS MemoryBase
;
759 UINT32 LowerMemorySize
;
761 UINT32 S3AcpiReservedMemoryBase
;
762 UINT32 S3AcpiReservedMemorySize
;
764 LowerMemorySize
= GetSystemMemorySizeBelow4gb (&mPlatformInfoHob
);
765 if (mPlatformInfoHob
.SmmSmramRequire
) {
767 // TSEG is chipped from the end of low RAM
769 LowerMemorySize
-= mPlatformInfoHob
.Q35TsegMbytes
* SIZE_1MB
;
772 S3AcpiReservedMemoryBase
= 0;
773 S3AcpiReservedMemorySize
= 0;
776 // If S3 is supported, then the S3 permanent PEI memory is placed next,
777 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
778 // is an approximation.
780 if (mPlatformInfoHob
.S3Supported
) {
781 S3AcpiReservedMemorySize
= SIZE_512KB
+
782 mPlatformInfoHob
.PcdCpuMaxLogicalProcessorNumber
*
783 PcdGet32 (PcdCpuApStackSize
);
784 S3AcpiReservedMemoryBase
= LowerMemorySize
- S3AcpiReservedMemorySize
;
785 LowerMemorySize
= S3AcpiReservedMemoryBase
;
788 mPlatformInfoHob
.S3AcpiReservedMemoryBase
= S3AcpiReservedMemoryBase
;
789 mPlatformInfoHob
.S3AcpiReservedMemorySize
= S3AcpiReservedMemorySize
;
791 if (mPlatformInfoHob
.BootMode
== BOOT_ON_S3_RESUME
) {
792 MemoryBase
= S3AcpiReservedMemoryBase
;
793 MemorySize
= S3AcpiReservedMemorySize
;
795 PeiMemoryCap
= GetPeiMemoryCap ();
798 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
800 mPlatformInfoHob
.PhysMemAddressWidth
,
805 // Determine the range of memory to use during PEI
807 // Technically we could lay the permanent PEI RAM over SEC's temporary
808 // decompression and scratch buffer even if "secure S3" is needed, since
809 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
810 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
811 // allocation HOB, and other allocations served from the permanent PEI RAM
812 // shouldn't overlap with that HOB.
814 MemoryBase
= mPlatformInfoHob
.S3Supported
&& mPlatformInfoHob
.SmmSmramRequire
?
815 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
816 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
817 MemorySize
= LowerMemorySize
- MemoryBase
;
818 if (MemorySize
> PeiMemoryCap
) {
819 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
820 MemorySize
= PeiMemoryCap
;
825 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
826 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
827 // permanent PEI RAM is located even higher.
829 if (mPlatformInfoHob
.SmmSmramRequire
&& mPlatformInfoHob
.Q35SmramAtDefaultSmbase
) {
830 ASSERT (SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
<= MemoryBase
);
834 // Publish this memory to the PEI Core
836 Status
= PublishSystemMemory (MemoryBase
, MemorySize
);
837 ASSERT_EFI_ERROR (Status
);
844 QemuInitializeRamBelow1gb (
845 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
848 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
849 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE
);
850 PlatformAddReservedMemoryBaseSizeHob (
852 MCH_DEFAULT_SMBASE_SIZE
,
856 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
< BASE_512KB
+ BASE_128KB
,
857 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
859 PlatformAddMemoryRangeHob (
860 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
,
861 BASE_512KB
+ BASE_128KB
864 PlatformAddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
869 Peform Memory Detection for QEMU / KVM
875 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
878 UINT64 LowerMemorySize
;
879 UINT64 UpperMemorySize
;
880 MTRR_SETTINGS MtrrSettings
;
883 DEBUG ((DEBUG_INFO
, "%a called\n", __FUNCTION__
));
886 // Determine total memory size available
888 LowerMemorySize
= GetSystemMemorySizeBelow4gb (PlatformInfoHob
);
890 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
892 // Create the following memory HOB as an exception on the S3 boot path.
894 // Normally we'd create memory HOBs only on the normal boot path. However,
895 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
896 // well, for "borrowing" a subset of it temporarily, for the AP startup
899 // CpuMpPei saves the original contents of the borrowed area in permanent
900 // PEI RAM, in a backup buffer allocated with the normal PEI services.
901 // CpuMpPei restores the original contents ("returns" the borrowed area) at
902 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
903 // transferring control to the OS's wakeup vector in the FACS.
905 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
906 // restore the original contents. Furthermore, we expect all such PEIMs
907 // (CpuMpPei included) to claim the borrowed areas by producing memory
908 // allocation HOBs, and to honor preexistent memory allocation HOBs when
909 // looking for an area to borrow.
911 QemuInitializeRamBelow1gb (PlatformInfoHob
);
914 // Create memory HOBs
916 QemuInitializeRamBelow1gb (PlatformInfoHob
);
918 if (PlatformInfoHob
->SmmSmramRequire
) {
921 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
922 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
- TsegSize
);
923 PlatformAddReservedMemoryBaseSizeHob (
924 LowerMemorySize
- TsegSize
,
929 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
);
933 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
934 // entries. Otherwise, create a single memory HOB with the flat >=4GB
935 // memory size read from the CMOS.
937 Status
= PlatformScanOrAdd64BitE820Ram (TRUE
, NULL
, NULL
);
938 if (EFI_ERROR (Status
)) {
939 UpperMemorySize
= PlatformGetSystemMemorySizeAbove4gb ();
940 if (UpperMemorySize
!= 0) {
941 PlatformAddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
947 // We'd like to keep the following ranges uncached:
949 // - [LowerMemorySize, 4 GB)
951 // Everything else should be WB. Unfortunately, programming the inverse (ie.
952 // keeping the default UC, and configuring the complement set of the above as
953 // WB) is not reliable in general, because the end of the upper RAM can have
954 // practically any alignment, and we may not have enough variable MTRRs to
957 if (IsMtrrSupported () && (PlatformInfoHob
->HostBridgeDevId
!= CLOUDHV_DEVICE_ID
)) {
958 MtrrGetAllMtrrs (&MtrrSettings
);
961 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
963 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
964 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
965 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
968 // flip default type to writeback
970 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
971 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
972 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
973 MtrrSetAllMtrrs (&MtrrSettings
);
976 // Set memory range from 640KB to 1MB to uncacheable
978 Status
= MtrrSetMemoryAttribute (
979 BASE_512KB
+ BASE_128KB
,
980 BASE_1MB
- (BASE_512KB
+ BASE_128KB
),
983 ASSERT_EFI_ERROR (Status
);
986 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
987 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
989 Status
= MtrrSetMemoryAttribute (
990 PlatformInfoHob
->Uc32Base
,
991 SIZE_4GB
- PlatformInfoHob
->Uc32Base
,
994 ASSERT_EFI_ERROR (Status
);
999 Publish system RAM and reserve memory regions
1003 InitializeRamRegions (
1004 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1007 QemuInitializeRam (PlatformInfoHob
);
1009 SevInitializeRam ();
1011 if (PlatformInfoHob
->S3Supported
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
1013 // This is the memory range that will be used for PEI on S3 resume
1015 BuildMemoryAllocationHob (
1016 PlatformInfoHob
->S3AcpiReservedMemoryBase
,
1017 PlatformInfoHob
->S3AcpiReservedMemorySize
,
1022 // Cover the initial RAM area used as stack and temporary PEI heap.
1024 // This is reserved as ACPI NVS so it can be used on S3 resume.
1026 BuildMemoryAllocationHob (
1027 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
1028 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
1033 // SEC stores its table of GUIDed section handlers here.
1035 BuildMemoryAllocationHob (
1036 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
1037 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
1043 // Reserve the initial page tables built by the reset vector code.
1045 // Since this memory range will be used by the Reset Vector on S3
1046 // resume, it must be reserved as ACPI NVS.
1048 BuildMemoryAllocationHob (
1049 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
),
1050 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesSize
),
1054 if (PlatformInfoHob
->SevEsIsEnabled
) {
1056 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1057 // includes the extra page table used to break down the 2MB page
1058 // mapping into 4KB page entries where the GHCB resides and the
1059 // GHCB area itself.
1061 // Since this memory range will be used by the Reset Vector on S3
1062 // resume, it must be reserved as ACPI NVS.
1064 BuildMemoryAllocationHob (
1065 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableBase
),
1066 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableSize
),
1069 BuildMemoryAllocationHob (
1070 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBase
),
1071 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbSize
),
1074 BuildMemoryAllocationHob (
1075 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupBase
),
1076 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupSize
),
1084 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
1085 if (!PlatformInfoHob
->SmmSmramRequire
) {
1087 // Reserve the lock box storage area
1089 // Since this memory range will be used on S3 resume, it must be
1090 // reserved as ACPI NVS.
1092 // If S3 is unsupported, then various drivers might still write to the
1093 // LockBox area. We ought to prevent DXE from serving allocation requests
1094 // such that they would overlap the LockBox storage.
1097 (VOID
*)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1098 (UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
)
1100 BuildMemoryAllocationHob (
1101 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1102 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
),
1103 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1107 if (PlatformInfoHob
->SmmSmramRequire
) {
1111 // Make sure the TSEG area that we reported as a reserved memory resource
1112 // cannot be used for reserved memory allocations.
1114 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
1115 BuildMemoryAllocationHob (
1116 GetSystemMemorySizeBelow4gb (PlatformInfoHob
) - TsegSize
,
1118 EfiReservedMemoryType
1121 // Similarly, allocate away the (already reserved) SMRAM at the default
1122 // SMBASE, if it exists.
1124 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
1125 BuildMemoryAllocationHob (
1127 MCH_DEFAULT_SMBASE_SIZE
,
1128 EfiReservedMemoryType
1134 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
1136 // Reserve the work area.
1138 // Since this memory range will be used by the Reset Vector on S3
1139 // resume, it must be reserved as ACPI NVS.
1141 // If S3 is unsupported, then various drivers might still write to the
1142 // work area. We ought to prevent DXE from serving allocation requests
1143 // such that they would overlap the work area.
1145 BuildMemoryAllocationHob (
1146 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
1147 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),
1148 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData