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
== INTEL_Q35_MCH_DEVICE_ID
) {
59 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
60 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) <= MAX_UINT32
);
61 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) >= PlatformInfoHob
->LowMemory
);
63 if (PlatformInfoHob
->LowMemory
<= BASE_2GB
) {
64 // Newer qemu with gigabyte aligned memory,
65 // 32-bit pci mmio window is 2G -> 4G then.
66 PlatformInfoHob
->Uc32Base
= BASE_2GB
;
69 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
70 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
71 // setting PcdPciExpressBaseAddress such that describing the
72 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
73 // variable MTRRs (preferably 1 or 2).
75 PlatformInfoHob
->Uc32Base
= (UINT32
)PcdGet64 (PcdPciExpressBaseAddress
);
81 if (PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) {
82 PlatformInfoHob
->Uc32Size
= CLOUDHV_MMIO_HOLE_SIZE
;
83 PlatformInfoHob
->Uc32Base
= CLOUDHV_MMIO_HOLE_ADDRESS
;
87 ASSERT (PlatformInfoHob
->HostBridgeDevId
== INTEL_82441_DEVICE_ID
);
89 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
90 // variable MTRR suffices by truncating the size to a whole power of two,
91 // while keeping the end affixed to 4GB. This will round the base up.
93 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
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
);
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 Iterate over the entries in QEMU's fw_cfg E820 RAM map, call the
221 passed callback for each entry.
223 @param[in] Callback The callback function to be called.
225 @param[in out] PlatformInfoHob PlatformInfo struct which is passed
226 through to the callback.
228 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
230 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
231 whole multiple of sizeof(EFI_E820_ENTRY64). No
232 RAM entry was processed.
234 @return Error codes from QemuFwCfgFindFile(). No RAM
240 IN E820_SCAN_CALLBACK Callback
,
241 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
245 FIRMWARE_CONFIG_ITEM FwCfgItem
;
247 EFI_E820_ENTRY64 E820Entry
;
250 Status
= QemuFwCfgFindFile ("etc/e820", &FwCfgItem
, &FwCfgSize
);
251 if (EFI_ERROR (Status
)) {
255 if (FwCfgSize
% sizeof E820Entry
!= 0) {
256 return EFI_PROTOCOL_ERROR
;
259 QemuFwCfgSelectItem (FwCfgItem
);
260 for (Processed
= 0; Processed
< FwCfgSize
; Processed
+= sizeof E820Entry
) {
261 QemuFwCfgReadBytes (sizeof E820Entry
, &E820Entry
);
262 Callback (&E820Entry
, PlatformInfoHob
);
271 @param Entries Pointer to PVH memmap
272 @param Count Number of entries
277 GetPvhMemmapEntries (
278 struct hvm_memmap_table_entry
**Entries
,
282 UINT32
*PVHResetVectorData
;
283 struct hvm_start_info
*pvh_start_info
;
285 PVHResetVectorData
= (VOID
*)(UINTN
)PcdGet32 (PcdXenPvhStartOfDayStructPtr
);
286 if (PVHResetVectorData
== 0) {
287 return EFI_NOT_FOUND
;
290 pvh_start_info
= (struct hvm_start_info
*)(UINTN
)PVHResetVectorData
[0];
292 *Entries
= (struct hvm_memmap_table_entry
*)(UINTN
)pvh_start_info
->memmap_paddr
;
293 *Count
= pvh_start_info
->memmap_entries
;
300 GetHighestSystemMemoryAddressFromPvhMemmap (
304 struct hvm_memmap_table_entry
*Memmap
;
305 UINT32 MemmapEntriesCount
;
306 struct hvm_memmap_table_entry
*Entry
;
309 UINT64 HighestAddress
;
314 Status
= GetPvhMemmapEntries (&Memmap
, &MemmapEntriesCount
);
315 ASSERT_EFI_ERROR (Status
);
317 for (Loop
= 0; Loop
< MemmapEntriesCount
; Loop
++) {
318 Entry
= Memmap
+ Loop
;
319 EntryEnd
= Entry
->addr
+ Entry
->size
;
321 if ((Entry
->type
== XEN_HVM_MEMMAP_TYPE_RAM
) &&
322 (EntryEnd
> HighestAddress
))
324 if (Below4gb
&& (EntryEnd
<= BASE_4GB
)) {
325 HighestAddress
= EntryEnd
;
326 } else if (!Below4gb
&& (EntryEnd
>= BASE_4GB
)) {
327 HighestAddress
= EntryEnd
;
332 return HighestAddress
;
337 PlatformGetSystemMemorySizeBelow4gb (
338 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
345 if ((PlatformInfoHob
->HostBridgeDevId
== CLOUDHV_DEVICE_ID
) &&
346 (CcProbe () != CcGuestTypeIntelTdx
))
348 // Get the information from PVH memmap
349 PlatformInfoHob
->LowMemory
= (UINT32
)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE
);
353 Status
= PlatformScanE820 (PlatformGetLowMemoryCB
, PlatformInfoHob
);
354 if (!EFI_ERROR (Status
) && (PlatformInfoHob
->LowMemory
> 0)) {
359 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
360 // * CMOS(0x35) is the high byte
361 // * CMOS(0x34) is the low byte
362 // * The size is specified in 64kb chunks
363 // * Since this is memory above 16MB, the 16MB must be added
364 // into the calculation to get the total memory size.
367 Cmos0x34
= (UINT8
)PlatformCmosRead8 (0x34);
368 Cmos0x35
= (UINT8
)PlatformCmosRead8 (0x35);
370 PlatformInfoHob
->LowMemory
= (UINT32
)(((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
375 PlatformGetSystemMemorySizeAbove4gb (
382 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
383 // * CMOS(0x5d) is the most significant size byte
384 // * CMOS(0x5c) is the middle size byte
385 // * CMOS(0x5b) is the least significant size byte
386 // * The size is specified in 64kb chunks
390 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
391 Size
= (UINT32
)(Size
<< 8) + (UINT32
)PlatformCmosRead8 (CmosIndex
);
394 return LShiftU64 (Size
, 16);
398 Return the highest address that DXE could possibly use, plus one.
402 PlatformGetFirstNonAddress (
403 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
406 UINT32 FwCfgPciMmio64Mb
;
408 FIRMWARE_CONFIG_ITEM FwCfgItem
;
410 UINT64 HotPlugMemoryEnd
;
413 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
414 // address from it. This can express an address >= 4GB+1TB.
416 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
417 // can only express a size smaller than 1TB), and add it to 4GB.
419 PlatformInfoHob
->FirstNonAddress
= BASE_4GB
;
420 Status
= PlatformScanE820 (PlatformGetFirstNonAddressCB
, PlatformInfoHob
);
421 if (EFI_ERROR (Status
)) {
422 PlatformInfoHob
->FirstNonAddress
= BASE_4GB
+ PlatformGetSystemMemorySizeAbove4gb ();
426 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
427 // resources to 32-bit anyway. See DegradeResource() in
428 // "PciResourceSupport.c".
431 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
438 // See if the user specified the number of megabytes for the 64-bit PCI host
439 // aperture. Accept an aperture size up to 16TB.
441 // As signaled by the "X-" prefix, this knob is experimental, and might go
444 Status
= QemuFwCfgParseUint32 (
445 "opt/ovmf/X-PciMmio64Mb",
450 case EFI_UNSUPPORTED
:
454 if (FwCfgPciMmio64Mb
<= 0x1000000) {
455 PlatformInfoHob
->PcdPciMmio64Size
= LShiftU64 (FwCfgPciMmio64Mb
, 20);
465 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
471 if (PlatformInfoHob
->PcdPciMmio64Size
== 0) {
472 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
475 "%a: disabling 64-bit PCI host aperture\n",
481 // There's nothing more to do; the amount of memory above 4GB fully
482 // determines the highest address plus one. The memory hotplug area (see
483 // below) plays no role for the firmware in this case.
489 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
490 // absolute, exclusive end address for the memory hotplug area. This area
491 // starts right at the end of the memory above 4GB. The 64-bit PCI host
492 // aperture must be placed above it.
494 Status
= QemuFwCfgFindFile (
495 "etc/reserved-memory-end",
499 if (!EFI_ERROR (Status
) && (FwCfgSize
== sizeof HotPlugMemoryEnd
)) {
500 QemuFwCfgSelectItem (FwCfgItem
);
501 QemuFwCfgReadBytes (FwCfgSize
, &HotPlugMemoryEnd
);
504 "%a: HotPlugMemoryEnd=0x%Lx\n",
509 ASSERT (HotPlugMemoryEnd
>= PlatformInfoHob
->FirstNonAddress
);
510 PlatformInfoHob
->FirstNonAddress
= HotPlugMemoryEnd
;
514 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
515 // that the host can map it with 1GB hugepages. Follow suit.
517 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->FirstNonAddress
, (UINT64
)SIZE_1GB
);
518 PlatformInfoHob
->PcdPciMmio64Size
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Size
, (UINT64
)SIZE_1GB
);
521 // The 64-bit PCI host aperture should also be "naturally" aligned. The
522 // alignment is determined by rounding the size of the aperture down to the
523 // next smaller or equal power of two. That is, align the aperture by the
524 // largest BAR size that can fit into it.
526 PlatformInfoHob
->PcdPciMmio64Base
= ALIGN_VALUE (PlatformInfoHob
->PcdPciMmio64Base
, GetPowerOfTwo64 (PlatformInfoHob
->PcdPciMmio64Size
));
529 // The useful address space ends with the 64-bit PCI host aperture.
531 PlatformInfoHob
->FirstNonAddress
= PlatformInfoHob
->PcdPciMmio64Base
+ PlatformInfoHob
->PcdPciMmio64Size
;
536 * Use CPUID to figure physical address width.
538 * Does *not* work reliable on qemu. For historical reasons qemu
539 * returns phys-bits=40 by default even in case the host machine
540 * supports less than that.
542 * So we apply the following rules (which can be enabled/disabled
543 * using the QemuQuirk parameter) to figure whenever we can work with
544 * the returned physical address width or not:
546 * (1) If it is 41 or higher consider it valid.
547 * (2) If it is 40 or lower consider it valid in case it matches a
548 * known-good value for the CPU vendor, which is:
549 * -> 36 or 39 for Intel
551 * (3) Otherwise consider it invalid.
553 * Recommendation: Run qemu with host-phys-bits=on. That will make
554 * sure guest phys-bits is not larger than host phys-bits. Some
555 * distro builds do that by default.
559 PlatformAddressWidthFromCpuid (
560 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
,
564 UINT32 RegEax
, RegEbx
, RegEcx
, RegEdx
, Max
;
566 CHAR8 Signature
[13] = { 0 };
567 BOOLEAN Valid
= FALSE
;
568 BOOLEAN Page1GSupport
= FALSE
;
570 AsmCpuid (0x80000000, &RegEax
, &RegEbx
, &RegEcx
, &RegEdx
);
571 *(UINT32
*)(Signature
+ 0) = RegEbx
;
572 *(UINT32
*)(Signature
+ 4) = RegEdx
;
573 *(UINT32
*)(Signature
+ 8) = RegEcx
;
576 if (Max
>= 0x80000001) {
577 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
578 if ((RegEdx
& BIT26
) != 0) {
579 Page1GSupport
= TRUE
;
583 if (Max
>= 0x80000008) {
584 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
585 PhysBits
= (UINT8
)RegEax
;
592 } else if (PhysBits
>= 41) {
594 } else if (AsciiStrCmp (Signature
, "GenuineIntel") == 0) {
595 if ((PhysBits
== 36) || (PhysBits
== 39)) {
598 } else if (AsciiStrCmp (Signature
, "AuthenticAMD") == 0) {
599 if (PhysBits
== 40) {
606 "%a: Signature: '%a', PhysBits: %d, QemuQuirk: %a, Valid: %a\n",
610 QemuQuirk
? "On" : "Off",
617 * Avoid 5-level paging altogether for now, which limits
618 * PhysBits to 48. Also avoid using address bit 48, due to sign
619 * extension we can't identity-map these addresses (and lots of
620 * places in edk2 assume we have everything identity-mapped).
621 * So the actual limit is 47.
623 DEBUG ((DEBUG_INFO
, "%a: limit PhysBits to 47 (avoid 5-level paging)\n", __func__
));
627 if (!Page1GSupport
&& (PhysBits
> 40)) {
628 DEBUG ((DEBUG_INFO
, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__
));
632 PlatformInfoHob
->PhysMemAddressWidth
= PhysBits
;
633 PlatformInfoHob
->FirstNonAddress
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
);
639 PlatformDynamicMmioWindow (
640 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
643 UINT64 AddrSpace
, MmioSpace
;
645 AddrSpace
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
);
646 MmioSpace
= LShiftU64 (1, PlatformInfoHob
->PhysMemAddressWidth
- 3);
648 if ((PlatformInfoHob
->PcdPciMmio64Size
< MmioSpace
) &&
649 (PlatformInfoHob
->PcdPciMmio64Base
+ MmioSpace
< AddrSpace
))
651 DEBUG ((DEBUG_INFO
, "%a: using dynamic mmio window\n", __func__
));
652 DEBUG ((DEBUG_INFO
, "%a: Addr Space 0x%Lx (%Ld GB)\n", __func__
, AddrSpace
, RShiftU64 (AddrSpace
, 30)));
653 DEBUG ((DEBUG_INFO
, "%a: MMIO Space 0x%Lx (%Ld GB)\n", __func__
, MmioSpace
, RShiftU64 (MmioSpace
, 30)));
654 PlatformInfoHob
->PcdPciMmio64Size
= MmioSpace
;
655 PlatformInfoHob
->PcdPciMmio64Base
= AddrSpace
- MmioSpace
;
657 DEBUG ((DEBUG_INFO
, "%a: using classic mmio window\n", __func__
));
660 DEBUG ((DEBUG_INFO
, "%a: Pci64 Base 0x%Lx\n", __func__
, PlatformInfoHob
->PcdPciMmio64Base
));
661 DEBUG ((DEBUG_INFO
, "%a: Pci64 Size 0x%Lx\n", __func__
, PlatformInfoHob
->PcdPciMmio64Size
));
665 Iterate over the PCI host bridges resources information optionally provided
666 in fw-cfg and find the highest address contained in the PCI MMIO windows. If
667 the information is found, return the exclusive end; one past the last usable
670 @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with
671 information extracted from host-provided data
672 or zero if no information available or an
675 @retval EFI_SUCCESS PCI information was read and the output
676 parameter updated with the last valid
677 address in the 64-bit MMIO range.
678 @retval EFI_INVALID_PARAMETER Pointer parameter is invalid
679 @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg
680 has an incompatible format
681 @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host
682 provided information, if any, cannot be
684 @retval EFI_NOT_FOUND No PCI host bridge information provided
689 PlatformScanHostProvided64BitPciMmioEnd (
690 OUT UINT64
*PciMmioAddressEnd
694 HOST_BRIDGE_INFO HostBridge
;
695 FIRMWARE_CONFIG_ITEM FwCfgItem
;
697 UINTN FwCfgReadIndex
;
699 UINT64 Above4GMmioEnd
;
701 if (PciMmioAddressEnd
== NULL
) {
702 return EFI_INVALID_PARAMETER
;
705 *PciMmioAddressEnd
= 0;
708 Status
= QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem
, &FwCfgSize
);
709 if (EFI_ERROR (Status
)) {
713 QemuFwCfgSelectItem (FwCfgItem
);
716 while (FwCfgReadIndex
< FwCfgSize
) {
717 Status
= QemuFwCfgReadNextHardwareInfoByType (
718 HardwareInfoTypeHostBridge
,
726 if (Status
!= EFI_SUCCESS
) {
728 // No more data available to read in the file, break
729 // loop and finish process
734 Status
= HardwareInfoPciHostBridgeLastMmioAddress (
741 if (Status
!= EFI_SUCCESS
) {
743 // Error parsing MMIO apertures and extracting last MMIO
744 // address, reset PciMmioAddressEnd as if no information was
745 // found, to avoid moving forward with incomplete data, and
750 "%a: ignoring malformed hardware information from fw_cfg\n",
753 *PciMmioAddressEnd
= 0;
757 if (Above4GMmioEnd
> *PciMmioAddressEnd
) {
758 *PciMmioAddressEnd
= Above4GMmioEnd
;
762 if (*PciMmioAddressEnd
> 0) {
764 // Host-provided PCI information was found and a MMIO window end
766 // Increase the End address by one to have the output pointing to
767 // one after the address in use (exclusive end).
769 *PciMmioAddressEnd
+= 1;
773 "%a: Pci64End=0x%Lx\n",
781 return EFI_NOT_FOUND
;
785 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
789 PlatformAddressWidthInitialization (
790 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
793 UINT8 PhysMemAddressWidth
;
796 if (PlatformInfoHob
->HostBridgeDevId
== 0xffff /* microvm */) {
797 PlatformAddressWidthFromCpuid (PlatformInfoHob
, FALSE
);
802 // First scan host-provided hardware information to assess if the address
803 // space is already known. If so, guest must use those values.
805 Status
= PlatformScanHostProvided64BitPciMmioEnd (&PlatformInfoHob
->FirstNonAddress
);
807 if (EFI_ERROR (Status
)) {
809 // If the host did not provide valid hardware information leading to a
810 // hard-defined 64-bit MMIO end, fold back to calculating the minimum range
812 // As guest-physical memory size grows, the permanent PEI RAM requirements
813 // are dominated by the identity-mapping page tables built by the DXE IPL.
814 // The DXL IPL keys off of the physical address bits advertized in the CPU
815 // HOB. To conserve memory, we calculate the minimum address width here.
817 PlatformGetFirstNonAddress (PlatformInfoHob
);
820 PlatformAddressWidthFromCpuid (PlatformInfoHob
, TRUE
);
821 if (PlatformInfoHob
->PhysMemAddressWidth
!= 0) {
822 // physical address width is known
823 PlatformDynamicMmioWindow (PlatformInfoHob
);
828 // physical address width is NOT known
829 // -> do some guess work, mostly based on installed memory
830 // -> try be conservstibe to stay below the guaranteed minimum of
831 // 36 phys bits (aka 64 GB).
833 PhysMemAddressWidth
= (UINT8
)HighBitSet64 (PlatformInfoHob
->FirstNonAddress
);
836 // If FirstNonAddress is not an integral power of two, then we need an
839 if ((PlatformInfoHob
->FirstNonAddress
& (PlatformInfoHob
->FirstNonAddress
- 1)) != 0) {
840 ++PhysMemAddressWidth
;
844 // The minimum address width is 36 (covers up to and excluding 64 GB, which
845 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
846 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
847 // can simply assert that here, since 48 bits are good enough for 256 TB.
849 if (PhysMemAddressWidth
<= 36) {
850 PhysMemAddressWidth
= 36;
853 #if defined (MDE_CPU_X64)
854 if (TdIsEnabled ()) {
855 if (TdSharedPageMask () == (1ULL << 47)) {
856 PhysMemAddressWidth
= 48;
858 PhysMemAddressWidth
= 52;
862 ASSERT (PhysMemAddressWidth
<= 52);
864 ASSERT (PhysMemAddressWidth
<= 48);
867 PlatformInfoHob
->PhysMemAddressWidth
= PhysMemAddressWidth
;
872 QemuInitializeRamBelow1gb (
873 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
876 if (PlatformInfoHob
->SmmSmramRequire
&& PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
877 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE
);
878 PlatformAddReservedMemoryBaseSizeHob (
880 MCH_DEFAULT_SMBASE_SIZE
,
884 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
< BASE_512KB
+ BASE_128KB
,
885 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
887 PlatformAddMemoryRangeHob (
888 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
,
889 BASE_512KB
+ BASE_128KB
892 PlatformAddMemoryRangeHob (0, BASE_512KB
+ BASE_128KB
);
897 Peform Memory Detection for QEMU / KVM
902 PlatformQemuInitializeRam (
903 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
906 UINT64 UpperMemorySize
;
907 MTRR_SETTINGS MtrrSettings
;
910 DEBUG ((DEBUG_INFO
, "%a called\n", __FUNCTION__
));
913 // Determine total memory size available
915 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
917 if (PlatformInfoHob
->BootMode
== BOOT_ON_S3_RESUME
) {
919 // Create the following memory HOB as an exception on the S3 boot path.
921 // Normally we'd create memory HOBs only on the normal boot path. However,
922 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
923 // well, for "borrowing" a subset of it temporarily, for the AP startup
926 // CpuMpPei saves the original contents of the borrowed area in permanent
927 // PEI RAM, in a backup buffer allocated with the normal PEI services.
928 // CpuMpPei restores the original contents ("returns" the borrowed area) at
929 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
930 // transferring control to the OS's wakeup vector in the FACS.
932 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
933 // restore the original contents. Furthermore, we expect all such PEIMs
934 // (CpuMpPei included) to claim the borrowed areas by producing memory
935 // allocation HOBs, and to honor preexistent memory allocation HOBs when
936 // looking for an area to borrow.
938 QemuInitializeRamBelow1gb (PlatformInfoHob
);
941 // Create memory HOBs
943 QemuInitializeRamBelow1gb (PlatformInfoHob
);
945 if (PlatformInfoHob
->SmmSmramRequire
) {
948 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
949 PlatformAddMemoryRangeHob (BASE_1MB
, PlatformInfoHob
->LowMemory
- TsegSize
);
950 PlatformAddReservedMemoryBaseSizeHob (
951 PlatformInfoHob
->LowMemory
- TsegSize
,
956 PlatformAddMemoryRangeHob (BASE_1MB
, PlatformInfoHob
->LowMemory
);
960 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
961 // entries. Otherwise, create a single memory HOB with the flat >=4GB
962 // memory size read from the CMOS.
964 Status
= PlatformScanE820 (PlatformAddHobCB
, PlatformInfoHob
);
965 if (EFI_ERROR (Status
)) {
966 UpperMemorySize
= PlatformGetSystemMemorySizeAbove4gb ();
967 if (UpperMemorySize
!= 0) {
968 PlatformAddMemoryBaseSizeHob (BASE_4GB
, UpperMemorySize
);
974 // We'd like to keep the following ranges uncached:
976 // - [Uc32Base, 4 GB)
978 // Everything else should be WB. Unfortunately, programming the inverse (ie.
979 // keeping the default UC, and configuring the complement set of the above as
980 // WB) is not reliable in general, because the end of the upper RAM can have
981 // practically any alignment, and we may not have enough variable MTRRs to
984 if (IsMtrrSupported () && (PlatformInfoHob
->HostBridgeDevId
!= CLOUDHV_DEVICE_ID
)) {
985 MtrrGetAllMtrrs (&MtrrSettings
);
988 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
990 ASSERT ((MtrrSettings
.MtrrDefType
& BIT11
) == 0);
991 ASSERT ((MtrrSettings
.MtrrDefType
& BIT10
) == 0);
992 ASSERT ((MtrrSettings
.MtrrDefType
& 0xFF) == 0);
995 // flip default type to writeback
997 SetMem (&MtrrSettings
.Fixed
, sizeof MtrrSettings
.Fixed
, 0x06);
998 ZeroMem (&MtrrSettings
.Variables
, sizeof MtrrSettings
.Variables
);
999 MtrrSettings
.MtrrDefType
|= BIT11
| BIT10
| 6;
1000 MtrrSetAllMtrrs (&MtrrSettings
);
1003 // Set memory range from 640KB to 1MB to uncacheable
1005 Status
= MtrrSetMemoryAttribute (
1006 BASE_512KB
+ BASE_128KB
,
1007 BASE_1MB
- (BASE_512KB
+ BASE_128KB
),
1010 ASSERT_EFI_ERROR (Status
);
1013 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
1014 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
1016 Status
= MtrrSetMemoryAttribute (
1017 PlatformInfoHob
->Uc32Base
,
1018 SIZE_4GB
- PlatformInfoHob
->Uc32Base
,
1021 ASSERT_EFI_ERROR (Status
);
1027 PlatformQemuInitializeRamForS3 (
1028 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
1031 if (PlatformInfoHob
->S3Supported
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
1033 // This is the memory range that will be used for PEI on S3 resume
1035 BuildMemoryAllocationHob (
1036 PlatformInfoHob
->S3AcpiReservedMemoryBase
,
1037 PlatformInfoHob
->S3AcpiReservedMemorySize
,
1042 // Cover the initial RAM area used as stack and temporary PEI heap.
1044 // This is reserved as ACPI NVS so it can be used on S3 resume.
1046 BuildMemoryAllocationHob (
1047 PcdGet32 (PcdOvmfSecPeiTempRamBase
),
1048 PcdGet32 (PcdOvmfSecPeiTempRamSize
),
1053 // SEC stores its table of GUIDed section handlers here.
1055 BuildMemoryAllocationHob (
1056 PcdGet64 (PcdGuidedExtractHandlerTableAddress
),
1057 PcdGet32 (PcdGuidedExtractHandlerTableSize
),
1063 // Reserve the initial page tables built by the reset vector code.
1065 // Since this memory range will be used by the Reset Vector on S3
1066 // resume, it must be reserved as ACPI NVS.
1068 BuildMemoryAllocationHob (
1069 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesBase
),
1070 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecPageTablesSize
),
1074 if (PlatformInfoHob
->SevEsIsEnabled
) {
1076 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1077 // includes the extra page table used to break down the 2MB page
1078 // mapping into 4KB page entries where the GHCB resides and the
1079 // GHCB area itself.
1081 // Since this memory range will be used by the Reset Vector on S3
1082 // resume, it must be reserved as ACPI NVS.
1084 BuildMemoryAllocationHob (
1085 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableBase
),
1086 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbPageTableSize
),
1089 BuildMemoryAllocationHob (
1090 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBase
),
1091 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbSize
),
1094 BuildMemoryAllocationHob (
1095 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupBase
),
1096 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSecGhcbBackupSize
),
1104 if (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
) {
1105 if (!PlatformInfoHob
->SmmSmramRequire
) {
1107 // Reserve the lock box storage area
1109 // Since this memory range will be used on S3 resume, it must be
1110 // reserved as ACPI NVS.
1112 // If S3 is unsupported, then various drivers might still write to the
1113 // LockBox area. We ought to prevent DXE from serving allocation requests
1114 // such that they would overlap the LockBox storage.
1117 (VOID
*)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1118 (UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
)
1120 BuildMemoryAllocationHob (
1121 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageBase
),
1122 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfLockBoxStorageSize
),
1123 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData
1127 if (PlatformInfoHob
->SmmSmramRequire
) {
1131 // Make sure the TSEG area that we reported as a reserved memory resource
1132 // cannot be used for reserved memory allocations.
1134 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob
);
1135 TsegSize
= PlatformInfoHob
->Q35TsegMbytes
* SIZE_1MB
;
1136 BuildMemoryAllocationHob (
1137 PlatformInfoHob
->LowMemory
- TsegSize
,
1139 EfiReservedMemoryType
1142 // Similarly, allocate away the (already reserved) SMRAM at the default
1143 // SMBASE, if it exists.
1145 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
1146 BuildMemoryAllocationHob (
1148 MCH_DEFAULT_SMBASE_SIZE
,
1149 EfiReservedMemoryType
1155 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
1157 // Reserve the work area.
1159 // Since this memory range will be used by the Reset Vector on S3
1160 // resume, it must be reserved as ACPI NVS.
1162 // If S3 is unsupported, then various drivers might still write to the
1163 // work area. We ought to prevent DXE from serving allocation requests
1164 // such that they would overlap the work area.
1166 BuildMemoryAllocationHob (
1167 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
1168 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),
1169 PlatformInfoHob
->S3Supported
? EfiACPIMemoryNVS
: EfiBootServicesData