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
);
128 QemuUc32BaseInitialization (
129 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
132 UINT32 LowerMemorySize
;
134 if (PlatformInfoHob
->HostBridgeDevId
== 0xffff /* microvm */) {
138 if (PlatformInfoHob
->HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
) {
140 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
141 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
142 // setting PcdPciExpressBaseAddress such that describing the
143 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
144 // variable MTRRs (preferably 1 or 2).
146 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress
) <= MAX_UINT32
);
147 PlatformInfoHob
->Uc32Base
= (UINT32
)FixedPcdGet64 (PcdPciExpressBaseAddress
);
151 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
152 PlatformInfoHob
->Uc32Size
= CLOUDHV_MMIO_HOLE_SIZE
;
153 PlatformInfoHob
->Uc32Base
= CLOUDHV_MMIO_HOLE_ADDRESS
;
157 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_82441_DEVICE_ID
);
159 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
160 // variable MTRR suffices by truncating the size to a whole power of two,
161 // while keeping the end affixed to 4GB. This will round the base up.
163 LowerMemorySize
= GetSystemMemorySizeBelow4gb (PlatformInfoHob
);
164 PlatformInfoHob
->Uc32Size
= GetPowerOfTwo32 ((UINT32
)(SIZE_4GB
- LowerMemorySize
));
165 PlatformInfoHob
->Uc32Base
= (UINT32
)(SIZE_4GB
- PlatformInfoHob
->Uc32Size
);
167 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
168 // Therefore mQemuUc32Base is at least 2GB.
170 ASSERT (PlatformInfoHob
->Uc32Base
>= BASE_2GB
);
172 if (PlatformInfoHob
->Uc32Base
!= LowerMemorySize
) {
175 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
176 "an UC32 size of 0x%x\n",
179 PlatformInfoHob
->Uc32Base
,
180 PlatformInfoHob
->Uc32Size
186 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
187 of the 32-bit address range.
189 Find the highest exclusive >=4GB RAM address, or produce memory resource
190 descriptor HOBs for RAM entries that start at or above 4GB.
192 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()
193 produces memory resource descriptor HOBs for RAM
194 entries that start at or above 4GB.
196 Otherwise, MaxAddress holds the highest exclusive
197 >=4GB RAM address on output. If QEMU's fw_cfg E820
198 RAM map contains no RAM entry that starts outside of
199 the 32-bit address range, then MaxAddress is exactly
202 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
204 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
205 whole multiple of sizeof(EFI_E820_ENTRY64). No
206 RAM entry was processed.
208 @return Error codes from QemuFwCfgFindFile(). No RAM
213 PlatformScanOrAdd64BitE820Ram (
214 IN BOOLEAN AddHighHob
,
215 OUT UINT64
*LowMemory OPTIONAL
,
216 OUT UINT64
*MaxAddress OPTIONAL
220 FIRMWARE_CONFIG_ITEM FwCfgItem
;
222 EFI_E820_ENTRY64 E820Entry
;
225 Status
= QemuFwCfgFindFile ("etc/e820", &FwCfgItem
, &FwCfgSize
);
226 if (EFI_ERROR (Status
)) {
230 if (FwCfgSize
% sizeof E820Entry
!= 0) {
231 return EFI_PROTOCOL_ERROR
;
234 if (LowMemory
!= NULL
) {
238 if (MaxAddress
!= NULL
) {
239 *MaxAddress
= BASE_4GB
;
242 QemuFwCfgSelectItem (FwCfgItem
);
243 for (Processed
= 0; Processed
< FwCfgSize
; Processed
+= sizeof E820Entry
) {
244 QemuFwCfgReadBytes (sizeof E820Entry
, &E820Entry
);
247 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
253 if (E820Entry
.Type
== EfiAcpiAddressRangeMemory
) {
254 if (AddHighHob
&& (E820Entry
.BaseAddr
>= BASE_4GB
)) {
259 // Round up the start address, and round down the end address.
261 Base
= ALIGN_VALUE (E820Entry
.BaseAddr
, (UINT64
)EFI_PAGE_SIZE
);
262 End
= (E820Entry
.BaseAddr
+ E820Entry
.Length
) &
263 ~(UINT64
)EFI_PAGE_MASK
;
265 PlatformAddMemoryRangeHob (Base
, End
);
268 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
276 if (MaxAddress
|| LowMemory
) {
279 Candidate
= E820Entry
.BaseAddr
+ E820Entry
.Length
;
280 if (MaxAddress
&& (Candidate
> *MaxAddress
)) {
281 *MaxAddress
= Candidate
;
284 "%a: MaxAddress=0x%Lx\n",
290 if (LowMemory
&& (Candidate
> *LowMemory
) && (Candidate
< BASE_4GB
)) {
291 *LowMemory
= Candidate
;
294 "%a: LowMemory=0x%Lx\n",
309 @param Entries Pointer to PVH memmap
310 @param Count Number of entries
315 GetPvhMemmapEntries (
316 struct hvm_memmap_table_entry
**Entries
,
320 UINT32
*PVHResetVectorData
;
321 struct hvm_start_info
*pvh_start_info
;
323 PVHResetVectorData
= (VOID
*)(UINTN
)PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
324 if (PVHResetVectorData
== 0) {
325 return EFI_NOT_FOUND
;
328 pvh_start_info
= (struct hvm_start_info
*)(UINTN
)PVHResetVectorData
[0];
330 *Entries
= (struct hvm_memmap_table_entry
*)(UINTN
)pvh_start_info
->memmap_paddr
;
331 *Count
= pvh_start_info
->memmap_entries
;
338 GetHighestSystemMemoryAddressFromPvhMemmap (
342 struct hvm_memmap_table_entry
*Memmap
;
343 UINT32 MemmapEntriesCount
;
344 struct hvm_memmap_table_entry
*Entry
;
347 UINT64 HighestAddress
;
352 Status
= GetPvhMemmapEntries (&Memmap
, &MemmapEntriesCount
);
353 ASSERT_EFI_ERROR (Status
);
355 for (Loop
= 0; Loop
< MemmapEntriesCount
; Loop
++) {
356 Entry
= Memmap
+ Loop
;
357 EntryEnd
= Entry
->addr
+ Entry
->size
;
359 if ((Entry
->type
== XEN_HVM_MEMMAP_TYPE_RAM
) &&
360 (EntryEnd
> HighestAddress
))
362 if (Below4gb
&& (EntryEnd
<= BASE_4GB
)) {
363 HighestAddress
= EntryEnd
;
364 } else if (!Below4gb
&& (EntryEnd
>= BASE_4GB
)) {
365 HighestAddress
= EntryEnd
;
370 return HighestAddress
;
374 GetSystemMemorySizeBelow4gb (
375 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
379 UINT64 LowerMemorySize
= 0;
383 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
384 // Get the information from PVH memmap
385 return (UINT32
)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE
);
388 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, &LowerMemorySize
, NULL
);
389 if ((Status
== EFI_SUCCESS
) && (LowerMemorySize
> 0)) {
390 return (UINT32
)LowerMemorySize
;
394 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
395 // * CMOS(0x35) is the high byte
396 // * CMOS(0x34) is the low byte
397 // * The size is specified in 64kb chunks
398 // * Since this is memory above 16MB, the 16MB must be added
399 // into the calculation to get the total memory size.
402 Cmos0x34
= (UINT8
)PlatformCmosRead8 (0x34);
403 Cmos0x35
= (UINT8
)PlatformCmosRead8 (0x35);
405 return (UINT32
)(((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
410 PlatformGetSystemMemorySizeAbove4gb (
417 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
418 // * CMOS(0x5d) is the most significant size byte
419 // * CMOS(0x5c) is the middle size byte
420 // * CMOS(0x5b) is the least significant size byte
421 // * The size is specified in 64kb chunks
425 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
426 Size
= (UINT32
)(Size
<< 8) + (UINT32
)PlatformCmosRead8 (CmosIndex
);
429 return LShiftU64 (Size
, 16);
433 Return the highest address that DXE could possibly use, plus one.
437 PlatformGetFirstNonAddress (
438 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
441 UINT64 FirstNonAddress
;
442 UINT32 FwCfgPciMmio64Mb
;
444 FIRMWARE_CONFIG_ITEM FwCfgItem
;
446 UINT64 HotPlugMemoryEnd
;
449 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
454 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
455 // address from it. This can express an address >= 4GB+1TB.
457 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
458 // can only express a size smaller than 1TB), and add it to 4GB.
460 Status
= PlatformScanOrAdd64BitE820Ram (FALSE
, NULL
, &FirstNonAddress
);
461 if (EFI_ERROR (Status
)) {
462 FirstNonAddress
= BASE_4GB
+ PlatformGetSystemMemorySizeAbove4gb ();
466 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
467 // resources to 32-bit anyway. See DegradeResource() in
468 // "PciResourceSupport.c".
471 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
472 return FirstNonAddress
;
478 // See if the user specified the number of megabytes for the 64-bit PCI host
479 // aperture. Accept an aperture size up to 16TB.
481 // As signaled by the "X-" prefix, this knob is experimental, and might go
484 Status
= QemuFwCfgParseUint32 (
485 "opt/ovmf/X-PciMmio64Mb",
490 case EFI_UNSUPPORTED
:
494 if (FwCfgPciMmio64Mb
<= 0x1000000) {
495 PlatformInfoHob
->PcdPciMmio64Size
= LShiftU64 (FwCfgPciMmio64Mb
, 20);
505 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
511 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
512 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
515 "%a: disabling 64-bit PCI host aperture\n",
521 // There's nothing more to do; the amount of memory above 4GB fully
522 // determines the highest address plus one. The memory hotplug area (see
523 // below) plays no role for the firmware in this case.
525 return FirstNonAddress
;
529 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
530 // absolute, exclusive end address for the memory hotplug area. This area
531 // starts right at the end of the memory above 4GB. The 64-bit PCI host
532 // aperture must be placed above it.
534 Status
= QemuFwCfgFindFile (
535 "etc/reserved-memory-end",
539 if (!EFI_ERROR (Status
) && (FwCfgSize
== sizeof HotPlugMemoryEnd
)) {
540 QemuFwCfgSelectItem (FwCfgItem
);
541 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
544 "%a: HotPlugMemoryEnd=0x%Lx\n",
549 ASSERT (HotPlugMemoryEnd
>= FirstNonAddress
);
550 FirstNonAddress
= HotPlugMemoryEnd
;
554 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
555 // that the host can map it with 1GB hugepages. Follow suit.
557 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (FirstNonAddress
, (UINT64
)SIZE_1GB
);
558 PlatformInfoHob
->PcdPciMmio64Size
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Size
, (UINT64
)SIZE_1GB
);
561 // The 64-bit PCI host aperture should also be "naturally" aligned. The
562 // alignment is determined by rounding the size of the aperture down to the
563 // next smaller or equal power of two. That is, align the aperture by the
564 // largest BAR size that can fit into it.
566 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Base
, GetPowerOfTwo64 (PlatformInfoHob
->PcdPciMmio64Size
));
569 // The useful address space ends with the 64-bit PCI host aperture.
571 FirstNonAddress
= PlatformInfoHob
->PcdPciMmio64Base
+ PlatformInfoHob
->PcdPciMmio64Size
;
572 return FirstNonAddress
;
576 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
580 PlatformAddressWidthInitialization (
581 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
584 UINT64 FirstNonAddress
;
585 UINT8 PhysMemAddressWidth
;
588 // As guest-physical memory size grows, the permanent PEI RAM requirements
589 // are dominated by the identity-mapping page tables built by the DXE IPL.
590 // The DXL IPL keys off of the physical address bits advertized in the CPU
591 // HOB. To conserve memory, we calculate the minimum address width here.
593 FirstNonAddress
= PlatformGetFirstNonAddress (PlatformInfoHob
);
594 PhysMemAddressWidth
= (UINT8
)HighBitSet64 (FirstNonAddress
);
597 // If FirstNonAddress is not an integral power of two, then we need an
600 if ((FirstNonAddress
& (FirstNonAddress
- 1)) != 0) {
601 ++PhysMemAddressWidth
;
605 // The minimum address width is 36 (covers up to and excluding 64 GB, which
606 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
607 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
608 // can simply assert that here, since 48 bits are good enough for 256 TB.
610 if (PhysMemAddressWidth
<= 36) {
611 PhysMemAddressWidth
= 36;
614 ASSERT (PhysMemAddressWidth
<= 48);
616 PlatformInfoHob
->FirstNonAddress
= FirstNonAddress
;
617 PlatformInfoHob
->PhysMemAddressWidth
= PhysMemAddressWidth
;
621 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
624 AddressWidthInitialization (
625 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
628 RETURN_STATUS PcdStatus
;
630 PlatformAddressWidthInitialization (PlatformInfoHob
);
633 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
634 // resources to 32-bit anyway. See DegradeResource() in
635 // "PciResourceSupport.c".
638 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
644 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
645 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
648 "%a: disabling 64-bit PCI host aperture\n",
651 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
652 ASSERT_RETURN_ERROR (PcdStatus
);
658 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
660 // The core PciHostBridgeDxe driver will automatically add this range to
661 // the GCD memory space map through our PciHostBridgeLib instance; here we
662 // only need to set the PCDs.
664 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, PlatformInfoHob
->PcdPciMmio64Base
);
665 ASSERT_RETURN_ERROR (PcdStatus
);
666 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, PlatformInfoHob
->PcdPciMmio64Size
);
667 ASSERT_RETURN_ERROR (PcdStatus
);
671 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
673 PlatformInfoHob
->PcdPciMmio64Base
,
674 PlatformInfoHob
->PcdPciMmio64Size
680 Calculate the cap for the permanent PEI memory.
688 BOOLEAN Page1GSupport
;
696 // If DXE is 32-bit, then just return the traditional 64 MB cap.
699 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
706 // Dependent on physical address width, PEI memory allocations can be
707 // dominated by the page tables built for 64-bit DXE. So we key the cap off
708 // of those. The code below is based on CreateIdentityMappingPageTables() in
709 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
711 Page1GSupport
= FALSE
;
712 if (PcdGetBool (PcdUse1GPageTable
)) {
713 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
714 if (RegEax
>= 0x80000001) {
715 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
716 if ((RegEdx
& BIT26
) != 0) {
717 Page1GSupport
= TRUE
;
722 if (mPlatformInfoHob
.PhysMemAddressWidth
<= 39) {
724 PdpEntries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 30);
725 ASSERT (PdpEntries
<= 0x200);
727 Pml4Entries
= 1 << (mPlatformInfoHob
.PhysMemAddressWidth
- 39);
728 ASSERT (Pml4Entries
<= 0x200);
732 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
733 (PdpEntries
+ 1) * Pml4Entries
+ 1;
734 ASSERT (TotalPages
<= 0x40201);
737 // Add 64 MB for miscellaneous allocations. Note that for
738 // PhysMemAddressWidth values close to 36, the cap will actually be
739 // dominated by this increment.
741 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
745 Publish PEI core memory
747 @return EFI_SUCCESS The PEIM initialized successfully.
756 EFI_PHYSICAL_ADDRESS MemoryBase
;
758 UINT32 LowerMemorySize
;
760 UINT32 S3AcpiReservedMemoryBase
;
761 UINT32 S3AcpiReservedMemorySize
;
763 LowerMemorySize
= GetSystemMemorySizeBelow4gb (&mPlatformInfoHob
);
764 if (mPlatformInfoHob
.SmmSmramRequire
) {
766 // TSEG is chipped from the end of low RAM
768 LowerMemorySize
-= mPlatformInfoHob
.Q35TsegMbytes
* SIZE_1MB
;
771 S3AcpiReservedMemoryBase
= 0;
772 S3AcpiReservedMemorySize
= 0;
775 // If S3 is supported, then the S3 permanent PEI memory is placed next,
776 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
777 // is an approximation.
779 if (mPlatformInfoHob
.S3Supported
) {
780 S3AcpiReservedMemorySize
= SIZE_512KB
+
781 mPlatformInfoHob
.PcdCpuMaxLogicalProcessorNumber
*
782 PcdGet32 (PcdCpuApStackSize
);
783 S3AcpiReservedMemoryBase
= LowerMemorySize
- S3AcpiReservedMemorySize
;
784 LowerMemorySize
= S3AcpiReservedMemoryBase
;
787 mPlatformInfoHob
.S3AcpiReservedMemoryBase
= S3AcpiReservedMemoryBase
;
788 mPlatformInfoHob
.S3AcpiReservedMemorySize
= S3AcpiReservedMemorySize
;
790 if (mPlatformInfoHob
.BootMode
== BOOT_ON_S3_RESUME
) {
791 MemoryBase
= S3AcpiReservedMemoryBase
;
792 MemorySize
= S3AcpiReservedMemorySize
;
794 PeiMemoryCap
= GetPeiMemoryCap ();
797 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
799 mPlatformInfoHob
.PhysMemAddressWidth
,
804 // Determine the range of memory to use during PEI
806 // Technically we could lay the permanent PEI RAM over SEC's temporary
807 // decompression and scratch buffer even if "secure S3" is needed, since
808 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
809 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
810 // allocation HOB, and other allocations served from the permanent PEI RAM
811 // shouldn't overlap with that HOB.
813 MemoryBase
= mPlatformInfoHob
.S3Supported
&& mPlatformInfoHob
.SmmSmramRequire
?
814 PcdGet32 (PcdOvmfDecompressionScratchEnd
) :
815 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
816 MemorySize
= LowerMemorySize
- MemoryBase
;
817 if (MemorySize
> PeiMemoryCap
) {
818 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
819 MemorySize
= PeiMemoryCap
;
824 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
825 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
826 // permanent PEI RAM is located even higher.
828 if (mPlatformInfoHob
.SmmSmramRequire
&& mPlatformInfoHob
.Q35SmramAtDefaultSmbase
) {
829 ASSERT (SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
<= MemoryBase
);
833 // Publish this memory to the PEI Core
835 Status
= PublishSystemMemory (MemoryBase
, MemorySize
);
836 ASSERT_EFI_ERROR (Status
);
843 QemuInitializeRamBelow1gb (
844 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
847 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
848 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE
);
849 PlatformAddReservedMemoryBaseSizeHob (
851 MCH_DEFAULT_SMBASE_SIZE
,
855 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
< BASE_512KB
+ BASE_128KB
,
856 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
858 PlatformAddMemoryRangeHob (
859 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
,
860 BASE_512KB
+ BASE_128KB
863 PlatformAddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
868 Peform Memory Detection for QEMU / KVM
874 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
877 UINT64 LowerMemorySize
;
878 UINT64 UpperMemorySize
;
879 MTRR_SETTINGS MtrrSettings
;
882 DEBUG ((DEBUG_INFO
, "%a called\n", __FUNCTION__
));
885 // Determine total memory size available
887 LowerMemorySize
= GetSystemMemorySizeBelow4gb (PlatformInfoHob
);
889 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
891 // Create the following memory HOB as an exception on the S3 boot path.
893 // Normally we'd create memory HOBs only on the normal boot path. However,
894 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
895 // well, for "borrowing" a subset of it temporarily, for the AP startup
898 // CpuMpPei saves the original contents of the borrowed area in permanent
899 // PEI RAM, in a backup buffer allocated with the normal PEI services.
900 // CpuMpPei restores the original contents ("returns" the borrowed area) at
901 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
902 // transferring control to the OS's wakeup vector in the FACS.
904 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
905 // restore the original contents. Furthermore, we expect all such PEIMs
906 // (CpuMpPei included) to claim the borrowed areas by producing memory
907 // allocation HOBs, and to honor preexistent memory allocation HOBs when
908 // looking for an area to borrow.
910 QemuInitializeRamBelow1gb (PlatformInfoHob
);
913 // Create memory HOBs
915 QemuInitializeRamBelow1gb (PlatformInfoHob
);
917 if (PlatformInfoHob
->SmmSmramRequire
) {
920 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
921 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
- TsegSize
);
922 PlatformAddReservedMemoryBaseSizeHob (
923 LowerMemorySize
- TsegSize
,
928 PlatformAddMemoryRangeHob (BASE_1MB
, LowerMemorySize
);
932 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
933 // entries. Otherwise, create a single memory HOB with the flat >=4GB
934 // memory size read from the CMOS.
936 Status
= PlatformScanOrAdd64BitE820Ram (TRUE
, NULL
, NULL
);
937 if (EFI_ERROR (Status
)) {
938 UpperMemorySize
= PlatformGetSystemMemorySizeAbove4gb ();
939 if (UpperMemorySize
!= 0) {
940 PlatformAddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
946 // We'd like to keep the following ranges uncached:
948 // - [LowerMemorySize, 4 GB)
950 // Everything else should be WB. Unfortunately, programming the inverse (ie.
951 // keeping the default UC, and configuring the complement set of the above as
952 // WB) is not reliable in general, because the end of the upper RAM can have
953 // practically any alignment, and we may not have enough variable MTRRs to
956 if (IsMtrrSupported () && (PlatformInfoHob
->HostBridgeDevId
!= CLOUDHV_DEVICE_ID
)) {
957 MtrrGetAllMtrrs (&MtrrSettings
);
960 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
962 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
963 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
964 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
967 // flip default type to writeback
969 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
970 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
971 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
972 MtrrSetAllMtrrs (&MtrrSettings
);
975 // Set memory range from 640KB to 1MB to uncacheable
977 Status
= MtrrSetMemoryAttribute (
978 BASE_512KB
+ BASE_128KB
,
979 BASE_1MB
- (BASE_512KB
+ BASE_128KB
),
982 ASSERT_EFI_ERROR (Status
);
985 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
986 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
988 Status
= MtrrSetMemoryAttribute (
989 PlatformInfoHob
->Uc32Base
,
990 SIZE_4GB
- PlatformInfoHob
->Uc32Base
,
993 ASSERT_EFI_ERROR (Status
);
998 Publish system RAM and reserve memory regions
1002 InitializeRamRegions (
1003 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1006 QemuInitializeRam (PlatformInfoHob
);
1008 SevInitializeRam ();
1010 if (PlatformInfoHob
->S3Supported
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
1012 // This is the memory range that will be used for PEI on S3 resume
1014 BuildMemoryAllocationHob (
1015 PlatformInfoHob
->S3AcpiReservedMemoryBase
,
1016 PlatformInfoHob
->S3AcpiReservedMemorySize
,
1021 // Cover the initial RAM area used as stack and temporary PEI heap.
1023 // This is reserved as ACPI NVS so it can be used on S3 resume.
1025 BuildMemoryAllocationHob (
1026 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
1027 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
1032 // SEC stores its table of GUIDed section handlers here.
1034 BuildMemoryAllocationHob (
1035 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
1036 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
1042 // Reserve the initial page tables built by the reset vector code.
1044 // Since this memory range will be used by the Reset Vector on S3
1045 // resume, it must be reserved as ACPI NVS.
1047 BuildMemoryAllocationHob (
1048 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
),
1049 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesSize
),
1053 if (PlatformInfoHob
->SevEsIsEnabled
) {
1055 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1056 // includes the extra page table used to break down the 2MB page
1057 // mapping into 4KB page entries where the GHCB resides and the
1058 // GHCB area itself.
1060 // Since this memory range will be used by the Reset Vector on S3
1061 // resume, it must be reserved as ACPI NVS.
1063 BuildMemoryAllocationHob (
1064 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableBase
),
1065 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableSize
),
1068 BuildMemoryAllocationHob (
1069 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBase
),
1070 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbSize
),
1073 BuildMemoryAllocationHob (
1074 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupBase
),
1075 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupSize
),
1083 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
1084 if (!PlatformInfoHob
->SmmSmramRequire
) {
1086 // Reserve the lock box storage area
1088 // Since this memory range will be used on S3 resume, it must be
1089 // reserved as ACPI NVS.
1091 // If S3 is unsupported, then various drivers might still write to the
1092 // LockBox area. We ought to prevent DXE from serving allocation requests
1093 // such that they would overlap the LockBox storage.
1096 (VOID
*)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1097 (UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
)
1099 BuildMemoryAllocationHob (
1100 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1101 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
),
1102 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1106 if (PlatformInfoHob
->SmmSmramRequire
) {
1110 // Make sure the TSEG area that we reported as a reserved memory resource
1111 // cannot be used for reserved memory allocations.
1113 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
1114 BuildMemoryAllocationHob (
1115 GetSystemMemorySizeBelow4gb (PlatformInfoHob
) - TsegSize
,
1117 EfiReservedMemoryType
1120 // Similarly, allocate away the (already reserved) SMRAM at the default
1121 // SMBASE, if it exists.
1123 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
1124 BuildMemoryAllocationHob (
1126 MCH_DEFAULT_SMBASE_SIZE
,
1127 EfiReservedMemoryType
1133 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
1135 // Reserve the work area.
1137 // Since this memory range will be used by the Reset Vector on S3
1138 // resume, it must be reserved as ACPI NVS.
1140 // If S3 is unsupported, then various drivers might still write to the
1141 // work area. We ought to prevent DXE from serving allocation requests
1142 // such that they would overlap the work area.
1144 BuildMemoryAllocationHob (
1145 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
1146 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),
1147 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData