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/CcProbeLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/HardwareInfoLib.h>
32 #include <Library/HobLib.h>
33 #include <Library/IoLib.h>
34 #include <Library/MemEncryptSevLib.h>
35 #include <Library/PcdLib.h>
36 #include <Library/PciLib.h>
37 #include <Library/PeimEntryPoint.h>
38 #include <Library/ResourcePublicationLib.h>
39 #include <Library/MtrrLib.h>
40 #include <Library/QemuFwCfgLib.h>
41 #include <Library/QemuFwCfgSimpleParserLib.h>
42 #include <Library/TdxLib.h>
44 #include <Library/PlatformInitLib.h>
46 #define MEGABYTE_SHIFT 20
50 PlatformQemuUc32BaseInitialization (
51 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
54 if (PlatformInfoHob
->HostBridgeDevId
== 0xffff /* microvm */) {
58 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
59 PlatformInfoHob
->Uc32Size
= CLOUDHV_MMIO_HOLE_SIZE
;
60 PlatformInfoHob
->Uc32Base
= CLOUDHV_MMIO_HOLE_ADDRESS
;
64 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
66 if (PlatformInfoHob
->HostBridgeDevId
== INTEL_Q35_MCH_DEVICE_ID
) {
67 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) <= MAX_UINT32
);
68 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) >= PlatformInfoHob
->LowMemory
);
70 if (PlatformInfoHob
->LowMemory
<= BASE_2GB
) {
71 // Newer qemu with gigabyte aligned memory,
72 // 32-bit pci mmio window is 2G -> 4G then.
73 PlatformInfoHob
->Uc32Base
= BASE_2GB
;
76 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
77 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
78 // setting PcdPciExpressBaseAddress such that describing the
79 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
80 // variable MTRRs (preferably 1 or 2).
82 PlatformInfoHob
->Uc32Base
= (UINT32
)PcdGet64 (PcdPciExpressBaseAddress
);
88 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_82441_DEVICE_ID
);
90 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
91 // variable MTRR suffices by truncating the size to a whole power of two,
92 // while keeping the end affixed to 4GB. This will round the base up.
94 PlatformInfoHob
->Uc32Size
= GetPowerOfTwo32 ((UINT32
)(SIZE_4GB
- PlatformInfoHob
->LowMemory
));
95 PlatformInfoHob
->Uc32Base
= (UINT32
)(SIZE_4GB
- PlatformInfoHob
->Uc32Size
);
97 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
98 // Therefore Uc32Base is at least 2GB.
100 ASSERT (PlatformInfoHob
->Uc32Base
>= BASE_2GB
);
102 if (PlatformInfoHob
->Uc32Base
!= PlatformInfoHob
->LowMemory
) {
105 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
106 "an UC32 size of 0x%x\n",
108 PlatformInfoHob
->LowMemory
,
109 PlatformInfoHob
->Uc32Base
,
110 PlatformInfoHob
->Uc32Size
115 typedef VOID (*E820_SCAN_CALLBACK
) (
116 EFI_E820_ENTRY64
*E820Entry
,
117 EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
121 Store first address not used by e820 RAM entries in
122 PlatformInfoHob->FirstNonAddress
126 PlatformGetFirstNonAddressCB (
127 IN EFI_E820_ENTRY64
*E820Entry
,
128 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
133 if (E820Entry
->Type
!= EfiAcpiAddressRangeMemory
) {
137 Candidate
= E820Entry
->BaseAddr
+ E820Entry
->Length
;
138 if (PlatformInfoHob
->FirstNonAddress
< Candidate
) {
139 DEBUG ((DEBUG_INFO
, "%a: FirstNonAddress=0x%Lx\n", __FUNCTION__
, Candidate
));
140 PlatformInfoHob
->FirstNonAddress
= Candidate
;
145 Store the low (below 4G) memory size in
146 PlatformInfoHob->LowMemory
150 PlatformGetLowMemoryCB (
151 IN EFI_E820_ENTRY64
*E820Entry
,
152 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
157 if (E820Entry
->Type
!= EfiAcpiAddressRangeMemory
) {
161 Candidate
= E820Entry
->BaseAddr
+ E820Entry
->Length
;
162 if (Candidate
>= BASE_4GB
) {
166 if (PlatformInfoHob
->LowMemory
< Candidate
) {
167 DEBUG ((DEBUG_INFO
, "%a: LowMemory=0x%Lx\n", __FUNCTION__
, Candidate
));
168 PlatformInfoHob
->LowMemory
= (UINT32
)Candidate
;
173 Create HOBs for reservations and RAM (except low memory).
178 IN EFI_E820_ENTRY64
*E820Entry
,
179 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
184 Base
= E820Entry
->BaseAddr
;
185 End
= E820Entry
->BaseAddr
+ E820Entry
->Length
;
187 switch (E820Entry
->Type
) {
188 case EfiAcpiAddressRangeMemory
:
189 if (Base
>= BASE_4GB
) {
191 // Round up the start address, and round down the end address.
193 Base
= ALIGN_VALUE (Base
, (UINT64
)EFI_PAGE_SIZE
);
194 End
= End
& ~(UINT64
)EFI_PAGE_MASK
;
196 DEBUG ((DEBUG_INFO
, "%a: HighMemory [0x%Lx, 0x%Lx)\n", __FUNCTION__
, Base
, End
));
197 PlatformAddMemoryRangeHob (Base
, End
);
202 case EfiAcpiAddressRangeReserved
:
203 BuildResourceDescriptorHob (EFI_RESOURCE_MEMORY_RESERVED
, 0, Base
, End
- Base
);
204 DEBUG ((DEBUG_INFO
, "%a: Reserved [0x%Lx, 0x%Lx)\n", __FUNCTION__
, Base
, End
));
209 "%a: Type %u [0x%Lx, 0x%Lx) (NOT HANDLED)\n",
220 Check whenever the 64bit PCI MMIO window overlaps with a reservation
221 from qemu. If so move down the MMIO window to resolve the conflict.
223 This happens on (virtual) AMD machines with 1TB address space,
224 because the AMD IOMMU uses an address window just below 1TB.
228 PlatformReservationConflictCB (
229 IN EFI_E820_ENTRY64
*E820Entry
,
230 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
233 UINT64 IntersectionBase
;
234 UINT64 IntersectionEnd
;
237 IntersectionBase
= MAX (
239 PlatformInfoHob
->PcdPciMmio64Base
241 IntersectionEnd
= MIN (
242 E820Entry
->BaseAddr
+ E820Entry
->Length
,
243 PlatformInfoHob
->PcdPciMmio64Base
+
244 PlatformInfoHob
->PcdPciMmio64Size
247 if (IntersectionBase
>= IntersectionEnd
) {
248 return; // no overlap
251 NewBase
= E820Entry
->BaseAddr
- PlatformInfoHob
->PcdPciMmio64Size
;
252 NewBase
= NewBase
& ~(PlatformInfoHob
->PcdPciMmio64Size
- 1);
256 "%a: move mmio: 0x%Lx => %Lx\n",
258 PlatformInfoHob
->PcdPciMmio64Base
,
261 PlatformInfoHob
->PcdPciMmio64Base
= NewBase
;
265 Iterate over the entries in QEMU's fw_cfg E820 RAM map, call the
266 passed callback for each entry.
268 @param[in] Callback The callback function to be called.
270 @param[in out] PlatformInfoHob PlatformInfo struct which is passed
271 through to the callback.
273 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
275 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
276 whole multiple of sizeof(EFI_E820_ENTRY64). No
277 RAM entry was processed.
279 @return Error codes from QemuFwCfgFindFile(). No RAM
285 IN E820_SCAN_CALLBACK Callback
,
286 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
290 FIRMWARE_CONFIG_ITEM FwCfgItem
;
292 EFI_E820_ENTRY64 E820Entry
;
295 Status
= QemuFwCfgFindFile ("etc/e820", &FwCfgItem
, &FwCfgSize
);
296 if (EFI_ERROR (Status
)) {
300 if (FwCfgSize
% sizeof E820Entry
!= 0) {
301 return EFI_PROTOCOL_ERROR
;
304 QemuFwCfgSelectItem (FwCfgItem
);
305 for (Processed
= 0; Processed
< FwCfgSize
; Processed
+= sizeof E820Entry
) {
306 QemuFwCfgReadBytes (sizeof E820Entry
, &E820Entry
);
307 Callback (&E820Entry
, PlatformInfoHob
);
316 @param Entries Pointer to PVH memmap
317 @param Count Number of entries
322 GetPvhMemmapEntries (
323 struct hvm_memmap_table_entry
**Entries
,
327 UINT32
*PVHResetVectorData
;
328 struct hvm_start_info
*pvh_start_info
;
330 PVHResetVectorData
= (VOID
*)(UINTN
)PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
331 if (PVHResetVectorData
== 0) {
332 return EFI_NOT_FOUND
;
335 pvh_start_info
= (struct hvm_start_info
*)(UINTN
)PVHResetVectorData
[0];
337 *Entries
= (struct hvm_memmap_table_entry
*)(UINTN
)pvh_start_info
->memmap_paddr
;
338 *Count
= pvh_start_info
->memmap_entries
;
345 GetHighestSystemMemoryAddressFromPvhMemmap (
349 struct hvm_memmap_table_entry
*Memmap
;
350 UINT32 MemmapEntriesCount
;
351 struct hvm_memmap_table_entry
*Entry
;
354 UINT64 HighestAddress
;
359 Status
= GetPvhMemmapEntries (&Memmap
, &MemmapEntriesCount
);
360 ASSERT_EFI_ERROR (Status
);
362 for (Loop
= 0; Loop
< MemmapEntriesCount
; Loop
++) {
363 Entry
= Memmap
+ Loop
;
364 EntryEnd
= Entry
->addr
+ Entry
->size
;
366 if ((Entry
->type
== XEN_HVM_MEMMAP_TYPE_RAM
) &&
367 (EntryEnd
> HighestAddress
))
369 if (Below4gb
&& (EntryEnd
<= BASE_4GB
)) {
370 HighestAddress
= EntryEnd
;
371 } else if (!Below4gb
&& (EntryEnd
>= BASE_4GB
)) {
372 HighestAddress
= EntryEnd
;
377 return HighestAddress
;
382 PlatformGetSystemMemorySizeBelow4gb (
383 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
390 if ((PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) &&
391 (CcProbe () != CcGuestTypeIntelTdx
))
393 // Get the information from PVH memmap
394 PlatformInfoHob
->LowMemory
= (UINT32
)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE
);
398 Status
= PlatformScanE820 (PlatformGetLowMemoryCB
, PlatformInfoHob
);
399 if (!EFI_ERROR (Status
) && (PlatformInfoHob
->LowMemory
> 0)) {
404 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
405 // * CMOS(0x35) is the high byte
406 // * CMOS(0x34) is the low byte
407 // * The size is specified in 64kb chunks
408 // * Since this is memory above 16MB, the 16MB must be added
409 // into the calculation to get the total memory size.
412 Cmos0x34
= (UINT8
)PlatformCmosRead8 (0x34);
413 Cmos0x35
= (UINT8
)PlatformCmosRead8 (0x35);
415 PlatformInfoHob
->LowMemory
= (UINT32
)(((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
420 PlatformGetSystemMemorySizeAbove4gb (
427 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
428 // * CMOS(0x5d) is the most significant size byte
429 // * CMOS(0x5c) is the middle size byte
430 // * CMOS(0x5b) is the least significant size byte
431 // * The size is specified in 64kb chunks
435 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
436 Size
= (UINT32
)(Size
<< 8) + (UINT32
)PlatformCmosRead8 (CmosIndex
);
439 return LShiftU64 (Size
, 16);
443 Return the highest address that DXE could possibly use, plus one.
447 PlatformGetFirstNonAddress (
448 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
451 UINT32 FwCfgPciMmio64Mb
;
453 FIRMWARE_CONFIG_ITEM FwCfgItem
;
455 UINT64 HotPlugMemoryEnd
;
458 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
459 // address from it. This can express an address >= 4GB+1TB.
461 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
462 // can only express a size smaller than 1TB), and add it to 4GB.
464 PlatformInfoHob
->FirstNonAddress
= BASE_4GB
;
465 Status
= PlatformScanE820 (PlatformGetFirstNonAddressCB
, PlatformInfoHob
);
466 if (EFI_ERROR (Status
)) {
467 PlatformInfoHob
->FirstNonAddress
= BASE_4GB
+ PlatformGetSystemMemorySizeAbove4gb ();
471 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
472 // resources to 32-bit anyway. See DegradeResource() in
473 // "PciResourceSupport.c".
476 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
483 // See if the user specified the number of megabytes for the 64-bit PCI host
484 // aperture. Accept an aperture size up to 16TB.
486 // As signaled by the "X-" prefix, this knob is experimental, and might go
489 Status
= QemuFwCfgParseUint32 (
490 "opt/ovmf/X-PciMmio64Mb",
495 case EFI_UNSUPPORTED
:
499 if (FwCfgPciMmio64Mb
<= 0x1000000) {
500 PlatformInfoHob
->PcdPciMmio64Size
= LShiftU64 (FwCfgPciMmio64Mb
, 20);
510 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
516 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
517 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
520 "%a: disabling 64-bit PCI host aperture\n",
526 // There's nothing more to do; the amount of memory above 4GB fully
527 // determines the highest address plus one. The memory hotplug area (see
528 // below) plays no role for the firmware in this case.
534 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
535 // absolute, exclusive end address for the memory hotplug area. This area
536 // starts right at the end of the memory above 4GB. The 64-bit PCI host
537 // aperture must be placed above it.
539 Status
= QemuFwCfgFindFile (
540 "etc/reserved-memory-end",
544 if (!EFI_ERROR (Status
) && (FwCfgSize
== sizeof HotPlugMemoryEnd
)) {
545 QemuFwCfgSelectItem (FwCfgItem
);
546 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
549 "%a: HotPlugMemoryEnd=0x%Lx\n",
554 ASSERT (HotPlugMemoryEnd
>= PlatformInfoHob
->FirstNonAddress
);
555 PlatformInfoHob
->FirstNonAddress
= HotPlugMemoryEnd
;
559 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
560 // that the host can map it with 1GB hugepages. Follow suit.
562 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->FirstNonAddress
, (UINT64
)SIZE_1GB
);
563 PlatformInfoHob
->PcdPciMmio64Size
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Size
, (UINT64
)SIZE_1GB
);
566 // The 64-bit PCI host aperture should also be "naturally" aligned. The
567 // alignment is determined by rounding the size of the aperture down to the
568 // next smaller or equal power of two. That is, align the aperture by the
569 // largest BAR size that can fit into it.
571 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Base
, GetPowerOfTwo64 (PlatformInfoHob
->PcdPciMmio64Size
));
574 // The useful address space ends with the 64-bit PCI host aperture.
576 PlatformInfoHob
->FirstNonAddress
= PlatformInfoHob
->PcdPciMmio64Base
+ PlatformInfoHob
->PcdPciMmio64Size
;
581 * Use CPUID to figure physical address width.
583 * Does *not* work reliable on qemu. For historical reasons qemu
584 * returns phys-bits=40 by default even in case the host machine
585 * supports less than that.
587 * So we apply the following rules (which can be enabled/disabled
588 * using the QemuQuirk parameter) to figure whenever we can work with
589 * the returned physical address width or not:
591 * (1) If it is 41 or higher consider it valid.
592 * (2) If it is 40 or lower consider it valid in case it matches a
593 * known-good value for the CPU vendor, which is:
594 * -> 36 or 39 for Intel
596 * (3) Otherwise consider it invalid.
598 * Recommendation: Run qemu with host-phys-bits=on. That will make
599 * sure guest phys-bits is not larger than host phys-bits. Some
600 * distro builds do that by default.
604 PlatformAddressWidthFromCpuid (
605 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
,
609 UINT32 RegEax
, RegEbx
, RegEcx
, RegEdx
, Max
;
611 CHAR8 Signature
[13] = { 0 };
612 BOOLEAN Valid
= FALSE
;
613 BOOLEAN Page1GSupport
= FALSE
;
615 AsmCpuid (0x80000000, &RegEax
, &RegEbx
, &RegEcx
, &RegEdx
);
616 *(UINT32
*)(Signature
+ 0) = RegEbx
;
617 *(UINT32
*)(Signature
+ 4) = RegEdx
;
618 *(UINT32
*)(Signature
+ 8) = RegEcx
;
621 if (Max
>= 0x80000001) {
622 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
623 if ((RegEdx
& BIT26
) != 0) {
624 Page1GSupport
= TRUE
;
628 if (Max
>= 0x80000008) {
629 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
630 PhysBits
= (UINT8
)RegEax
;
637 } else if (PhysBits
>= 41) {
639 } else if (AsciiStrCmp (Signature
, "GenuineIntel") == 0) {
640 if ((PhysBits
== 36) || (PhysBits
== 39)) {
643 } else if (AsciiStrCmp (Signature
, "AuthenticAMD") == 0) {
644 if (PhysBits
== 40) {
651 "%a: Signature: '%a', PhysBits: %d, QemuQuirk: %a, Valid: %a\n",
655 QemuQuirk
? "On" : "Off",
662 * Avoid 5-level paging altogether for now, which limits
663 * PhysBits to 48. Also avoid using address bit 48, due to sign
664 * extension we can't identity-map these addresses (and lots of
665 * places in edk2 assume we have everything identity-mapped).
666 * So the actual limit is 47.
668 DEBUG ((DEBUG_INFO
, "%a: limit PhysBits to 47 (avoid 5-level paging)\n", __func__
));
672 if (!Page1GSupport
&& (PhysBits
> 40)) {
673 DEBUG ((DEBUG_INFO
, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__
));
677 PlatformInfoHob
->PhysMemAddressWidth
= PhysBits
;
678 PlatformInfoHob
->FirstNonAddress
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
);
684 PlatformDynamicMmioWindow (
685 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
688 UINT64 AddrSpace
, MmioSpace
;
690 AddrSpace
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
);
691 MmioSpace
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
- 3);
693 if ((PlatformInfoHob
->PcdPciMmio64Size
< MmioSpace
) &&
694 (PlatformInfoHob
->PcdPciMmio64Base
+ MmioSpace
< AddrSpace
))
696 DEBUG ((DEBUG_INFO
, "%a: using dynamic mmio window\n", __func__
));
697 DEBUG ((DEBUG_INFO
, "%a: Addr Space 0x%Lx (%Ld GB)\n", __func__
, AddrSpace
, RShiftU64 (AddrSpace
, 30)));
698 DEBUG ((DEBUG_INFO
, "%a: MMIO Space 0x%Lx (%Ld GB)\n", __func__
, MmioSpace
, RShiftU64 (MmioSpace
, 30)));
699 PlatformInfoHob
->PcdPciMmio64Size
= MmioSpace
;
700 PlatformInfoHob
->PcdPciMmio64Base
= AddrSpace
- MmioSpace
;
701 PlatformScanE820 (PlatformReservationConflictCB
, PlatformInfoHob
);
703 DEBUG ((DEBUG_INFO
, "%a: using classic mmio window\n", __func__
));
706 DEBUG ((DEBUG_INFO
, "%a: Pci64 Base 0x%Lx\n", __func__
, PlatformInfoHob
->PcdPciMmio64Base
));
707 DEBUG ((DEBUG_INFO
, "%a: Pci64 Size 0x%Lx\n", __func__
, PlatformInfoHob
->PcdPciMmio64Size
));
711 Iterate over the PCI host bridges resources information optionally provided
712 in fw-cfg and find the highest address contained in the PCI MMIO windows. If
713 the information is found, return the exclusive end; one past the last usable
716 @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with
717 information extracted from host-provided data
718 or zero if no information available or an
721 @retval EFI_SUCCESS PCI information was read and the output
722 parameter updated with the last valid
723 address in the 64-bit MMIO range.
724 @retval EFI_INVALID_PARAMETER Pointer parameter is invalid
725 @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg
726 has an incompatible format
727 @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host
728 provided information, if any, cannot be
730 @retval EFI_NOT_FOUND No PCI host bridge information provided
735 PlatformScanHostProvided64BitPciMmioEnd (
736 OUT UINT64
*PciMmioAddressEnd
740 HOST_BRIDGE_INFO HostBridge
;
741 FIRMWARE_CONFIG_ITEM FwCfgItem
;
743 UINTN FwCfgReadIndex
;
745 UINT64 Above4GMmioEnd
;
747 if (PciMmioAddressEnd
== NULL
) {
748 return EFI_INVALID_PARAMETER
;
751 *PciMmioAddressEnd
= 0;
754 Status
= QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem
, &FwCfgSize
);
755 if (EFI_ERROR (Status
)) {
759 QemuFwCfgSelectItem (FwCfgItem
);
762 while (FwCfgReadIndex
< FwCfgSize
) {
763 Status
= QemuFwCfgReadNextHardwareInfoByType (
764 HardwareInfoTypeHostBridge
,
772 if (Status
!= EFI_SUCCESS
) {
774 // No more data available to read in the file, break
775 // loop and finish process
780 Status
= HardwareInfoPciHostBridgeLastMmioAddress (
787 if (Status
!= EFI_SUCCESS
) {
789 // Error parsing MMIO apertures and extracting last MMIO
790 // address, reset PciMmioAddressEnd as if no information was
791 // found, to avoid moving forward with incomplete data, and
796 "%a: ignoring malformed hardware information from fw_cfg\n",
799 *PciMmioAddressEnd
= 0;
803 if (Above4GMmioEnd
> *PciMmioAddressEnd
) {
804 *PciMmioAddressEnd
= Above4GMmioEnd
;
808 if (*PciMmioAddressEnd
> 0) {
810 // Host-provided PCI information was found and a MMIO window end
812 // Increase the End address by one to have the output pointing to
813 // one after the address in use (exclusive end).
815 *PciMmioAddressEnd
+= 1;
819 "%a: Pci64End=0x%Lx\n",
827 return EFI_NOT_FOUND
;
831 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
835 PlatformAddressWidthInitialization (
836 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
839 UINT8 PhysMemAddressWidth
;
842 if (PlatformInfoHob
->HostBridgeDevId
== 0xffff /* microvm */) {
843 PlatformAddressWidthFromCpuid (PlatformInfoHob
, FALSE
);
848 // First scan host-provided hardware information to assess if the address
849 // space is already known. If so, guest must use those values.
851 Status
= PlatformScanHostProvided64BitPciMmioEnd (&PlatformInfoHob
->FirstNonAddress
);
853 if (EFI_ERROR (Status
)) {
855 // If the host did not provide valid hardware information leading to a
856 // hard-defined 64-bit MMIO end, fold back to calculating the minimum range
858 // As guest-physical memory size grows, the permanent PEI RAM requirements
859 // are dominated by the identity-mapping page tables built by the DXE IPL.
860 // The DXL IPL keys off of the physical address bits advertized in the CPU
861 // HOB. To conserve memory, we calculate the minimum address width here.
863 PlatformGetFirstNonAddress (PlatformInfoHob
);
866 PlatformAddressWidthFromCpuid (PlatformInfoHob
, TRUE
);
867 if (PlatformInfoHob
->PhysMemAddressWidth
!= 0) {
868 // physical address width is known
869 PlatformDynamicMmioWindow (PlatformInfoHob
);
874 // physical address width is NOT known
875 // -> do some guess work, mostly based on installed memory
876 // -> try be conservstibe to stay below the guaranteed minimum of
877 // 36 phys bits (aka 64 GB).
879 PhysMemAddressWidth
= (UINT8
)HighBitSet64 (PlatformInfoHob
->FirstNonAddress
);
882 // If FirstNonAddress is not an integral power of two, then we need an
885 if ((PlatformInfoHob
->FirstNonAddress
& (PlatformInfoHob
->FirstNonAddress
- 1)) != 0) {
886 ++PhysMemAddressWidth
;
890 // The minimum address width is 36 (covers up to and excluding 64 GB, which
891 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
892 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
893 // can simply assert that here, since 48 bits are good enough for 256 TB.
895 if (PhysMemAddressWidth
<= 36) {
896 PhysMemAddressWidth
= 36;
899 #if defined (MDE_CPU_X64)
900 if (TdIsEnabled ()) {
901 if (TdSharedPageMask () == (1ULL << 47)) {
902 PhysMemAddressWidth
= 48;
904 PhysMemAddressWidth
= 52;
908 ASSERT (PhysMemAddressWidth
<= 52);
910 ASSERT (PhysMemAddressWidth
<= 48);
913 PlatformInfoHob
->PhysMemAddressWidth
= PhysMemAddressWidth
;
918 QemuInitializeRamBelow1gb (
919 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
922 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
923 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE
);
924 PlatformAddReservedMemoryBaseSizeHob (
926 MCH_DEFAULT_SMBASE_SIZE
,
930 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
< BASE_512KB
+ BASE_128KB
,
931 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
933 PlatformAddMemoryRangeHob (
934 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
,
935 BASE_512KB
+ BASE_128KB
938 PlatformAddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
943 Peform Memory Detection for QEMU / KVM
948 PlatformQemuInitializeRam (
949 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
952 UINT64 UpperMemorySize
;
953 MTRR_SETTINGS MtrrSettings
;
956 DEBUG ((DEBUG_INFO
, "%a called\n", __FUNCTION__
));
959 // Determine total memory size available
961 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
963 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
965 // Create the following memory HOB as an exception on the S3 boot path.
967 // Normally we'd create memory HOBs only on the normal boot path. However,
968 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
969 // well, for "borrowing" a subset of it temporarily, for the AP startup
972 // CpuMpPei saves the original contents of the borrowed area in permanent
973 // PEI RAM, in a backup buffer allocated with the normal PEI services.
974 // CpuMpPei restores the original contents ("returns" the borrowed area) at
975 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
976 // transferring control to the OS's wakeup vector in the FACS.
978 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
979 // restore the original contents. Furthermore, we expect all such PEIMs
980 // (CpuMpPei included) to claim the borrowed areas by producing memory
981 // allocation HOBs, and to honor preexistent memory allocation HOBs when
982 // looking for an area to borrow.
984 QemuInitializeRamBelow1gb (PlatformInfoHob
);
987 // Create memory HOBs
989 QemuInitializeRamBelow1gb (PlatformInfoHob
);
991 if (PlatformInfoHob
->SmmSmramRequire
) {
994 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
995 PlatformAddMemoryRangeHob (BASE_1MB
, PlatformInfoHob
->LowMemory
- TsegSize
);
996 PlatformAddReservedMemoryBaseSizeHob (
997 PlatformInfoHob
->LowMemory
- TsegSize
,
1002 PlatformAddMemoryRangeHob (BASE_1MB
, PlatformInfoHob
->LowMemory
);
1006 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
1007 // entries. Otherwise, create a single memory HOB with the flat >=4GB
1008 // memory size read from the CMOS.
1010 Status
= PlatformScanE820 (PlatformAddHobCB
, PlatformInfoHob
);
1011 if (EFI_ERROR (Status
)) {
1012 UpperMemorySize
= PlatformGetSystemMemorySizeAbove4gb ();
1013 if (UpperMemorySize
!= 0) {
1014 PlatformAddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
1020 // We'd like to keep the following ranges uncached:
1022 // - [Uc32Base, 4 GB)
1024 // Everything else should be WB. Unfortunately, programming the inverse (ie.
1025 // keeping the default UC, and configuring the complement set of the above as
1026 // WB) is not reliable in general, because the end of the upper RAM can have
1027 // practically any alignment, and we may not have enough variable MTRRs to
1028 // cover it exactly.
1030 if (IsMtrrSupported () && (PlatformInfoHob
->HostBridgeDevId
!= CLOUDHV_DEVICE_ID
)) {
1031 MtrrGetAllMtrrs (&MtrrSettings
);
1034 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
1036 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
1037 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
1038 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
1041 // flip default type to writeback
1043 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
1044 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
1045 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
1046 MtrrSetAllMtrrs (&MtrrSettings
);
1049 // Set memory range from 640KB to 1MB to uncacheable
1051 Status
= MtrrSetMemoryAttribute (
1052 BASE_512KB
+ BASE_128KB
,
1053 BASE_1MB
- (BASE_512KB
+ BASE_128KB
),
1056 ASSERT_EFI_ERROR (Status
);
1059 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
1060 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
1062 Status
= MtrrSetMemoryAttribute (
1063 PlatformInfoHob
->Uc32Base
,
1064 SIZE_4GB
- PlatformInfoHob
->Uc32Base
,
1067 ASSERT_EFI_ERROR (Status
);
1073 PlatformQemuInitializeRamForS3 (
1074 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1077 if (PlatformInfoHob
->S3Supported
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
1079 // This is the memory range that will be used for PEI on S3 resume
1081 BuildMemoryAllocationHob (
1082 PlatformInfoHob
->S3AcpiReservedMemoryBase
,
1083 PlatformInfoHob
->S3AcpiReservedMemorySize
,
1088 // Cover the initial RAM area used as stack and temporary PEI heap.
1090 // This is reserved as ACPI NVS so it can be used on S3 resume.
1092 BuildMemoryAllocationHob (
1093 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
1094 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
1099 // SEC stores its table of GUIDed section handlers here.
1101 BuildMemoryAllocationHob (
1102 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
1103 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
1109 // Reserve the initial page tables built by the reset vector code.
1111 // Since this memory range will be used by the Reset Vector on S3
1112 // resume, it must be reserved as ACPI NVS.
1114 BuildMemoryAllocationHob (
1115 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
),
1116 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesSize
),
1120 if (PlatformInfoHob
->SevEsIsEnabled
) {
1122 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1123 // includes the extra page table used to break down the 2MB page
1124 // mapping into 4KB page entries where the GHCB resides and the
1125 // GHCB area itself.
1127 // Since this memory range will be used by the Reset Vector on S3
1128 // resume, it must be reserved as ACPI NVS.
1130 BuildMemoryAllocationHob (
1131 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableBase
),
1132 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableSize
),
1135 BuildMemoryAllocationHob (
1136 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBase
),
1137 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbSize
),
1140 BuildMemoryAllocationHob (
1141 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupBase
),
1142 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupSize
),
1150 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
1151 if (!PlatformInfoHob
->SmmSmramRequire
) {
1153 // Reserve the lock box storage area
1155 // Since this memory range will be used on S3 resume, it must be
1156 // reserved as ACPI NVS.
1158 // If S3 is unsupported, then various drivers might still write to the
1159 // LockBox area. We ought to prevent DXE from serving allocation requests
1160 // such that they would overlap the LockBox storage.
1163 (VOID
*)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1164 (UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
)
1166 BuildMemoryAllocationHob (
1167 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1168 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
),
1169 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1173 if (PlatformInfoHob
->SmmSmramRequire
) {
1177 // Make sure the TSEG area that we reported as a reserved memory resource
1178 // cannot be used for reserved memory allocations.
1180 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
1181 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
1182 BuildMemoryAllocationHob (
1183 PlatformInfoHob
->LowMemory
- TsegSize
,
1185 EfiReservedMemoryType
1188 // Similarly, allocate away the (already reserved) SMRAM at the default
1189 // SMBASE, if it exists.
1191 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
1192 BuildMemoryAllocationHob (
1194 MCH_DEFAULT_SMBASE_SIZE
,
1195 EfiReservedMemoryType
1201 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
1203 // Reserve the work area.
1205 // Since this memory range will be used by the Reset Vector on S3
1206 // resume, it must be reserved as ACPI NVS.
1208 // If S3 is unsupported, then various drivers might still write to the
1209 // work area. We ought to prevent DXE from serving allocation requests
1210 // such that they would overlap the work area.
1212 BuildMemoryAllocationHob (
1213 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
1214 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),
1215 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData