diff --git a/OvmfPkg/Library/PlatformInitLib/MemDetect.c b/OvmfPkg/Library/PlatformInitLib/MemDetect.c index c28d7601f8..942eaf89cf 100644 --- a/OvmfPkg/Library/PlatformInitLib/MemDetect.c +++ b/OvmfPkg/Library/PlatformInitLib/MemDetect.c @@ -27,6 +27,7 @@ Module Name: #include #include #include +#include #include #include #include @@ -527,6 +528,126 @@ PlatformAddressWidthFromCpuid ( )); } +/** + Iterate over the PCI host bridges resources information optionally provided + in fw-cfg and find the highest address contained in the PCI MMIO windows. If + the information is found, return the exclusive end; one past the last usable + address. + + @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with + information extracted from host-provided data + or zero if no information available or an + error happened + + @retval EFI_SUCCESS PCI information was read and the output + parameter updated with the last valid + address in the 64-bit MMIO range. + @retval EFI_INVALID_PARAMETER Pointer parameter is invalid + @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg + has an incompatible format + @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host + provided information, if any, cannot be + read + @retval EFI_NOT_FOUND No PCI host bridge information provided + by the host. +**/ +STATIC +EFI_STATUS +PlatformScanHostProvided64BitPciMmioEnd ( + OUT UINT64 *PciMmioAddressEnd + ) +{ + EFI_STATUS Status; + HOST_BRIDGE_INFO HostBridge; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINTN FwCfgReadIndex; + UINTN ReadDataSize; + UINT64 Above4GMmioEnd; + + if (PciMmioAddressEnd == NULL) { + return EFI_INVALID_PARAMETER; + } + + *PciMmioAddressEnd = 0; + Above4GMmioEnd = 0; + + Status = QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + return Status; + } + + QemuFwCfgSelectItem (FwCfgItem); + + FwCfgReadIndex = 0; + while (FwCfgReadIndex < FwCfgSize) { + Status = QemuFwCfgReadNextHardwareInfoByType ( + HardwareInfoTypeHostBridge, + sizeof (HostBridge), + FwCfgSize, + &HostBridge, + &ReadDataSize, + &FwCfgReadIndex + ); + + if (Status != EFI_SUCCESS) { + // + // No more data available to read in the file, break + // loop and finish process + // + break; + } + + Status = HardwareInfoPciHostBridgeLastMmioAddress ( + &HostBridge, + ReadDataSize, + TRUE, + &Above4GMmioEnd + ); + + if (Status != EFI_SUCCESS) { + // + // Error parsing MMIO apertures and extracting last MMIO + // address, reset PciMmioAddressEnd as if no information was + // found, to avoid moving forward with incomplete data, and + // bail out + // + DEBUG (( + DEBUG_ERROR, + "%a: ignoring malformed hardware information from fw_cfg\n", + __FUNCTION__ + )); + *PciMmioAddressEnd = 0; + return Status; + } + + if (Above4GMmioEnd > *PciMmioAddressEnd) { + *PciMmioAddressEnd = Above4GMmioEnd; + } + } + + if (*PciMmioAddressEnd > 0) { + // + // Host-provided PCI information was found and a MMIO window end + // derived from it. + // Increase the End address by one to have the output pointing to + // one after the address in use (exclusive end). + // + *PciMmioAddressEnd += 1; + + DEBUG (( + DEBUG_INFO, + "%a: Pci64End=0x%Lx\n", + __FUNCTION__, + *PciMmioAddressEnd + )); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + /** Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size. **/ @@ -536,8 +657,9 @@ PlatformAddressWidthInitialization ( IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob ) { - UINT64 FirstNonAddress; - UINT8 PhysMemAddressWidth; + UINT64 FirstNonAddress; + UINT8 PhysMemAddressWidth; + EFI_STATUS Status; if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) { PlatformAddressWidthFromCpuid (PlatformInfoHob); @@ -545,12 +667,24 @@ PlatformAddressWidthInitialization ( } // - // As guest-physical memory size grows, the permanent PEI RAM requirements - // are dominated by the identity-mapping page tables built by the DXE IPL. - // The DXL IPL keys off of the physical address bits advertized in the CPU - // HOB. To conserve memory, we calculate the minimum address width here. + // First scan host-provided hardware information to assess if the address + // space is already known. If so, guest must use those values. // - FirstNonAddress = PlatformGetFirstNonAddress (PlatformInfoHob); + Status = PlatformScanHostProvided64BitPciMmioEnd (&FirstNonAddress); + + if (EFI_ERROR (Status)) { + // + // If the host did not provide valid hardware information leading to a + // hard-defined 64-bit MMIO end, fold back to calculating the minimum range + // needed. + // As guest-physical memory size grows, the permanent PEI RAM requirements + // are dominated by the identity-mapping page tables built by the DXE IPL. + // The DXL IPL keys off of the physical address bits advertized in the CPU + // HOB. To conserve memory, we calculate the minimum address width here. + // + FirstNonAddress = PlatformGetFirstNonAddress (PlatformInfoHob); + } + PhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress); // diff --git a/OvmfPkg/Library/PlatformInitLib/PlatformInitLib.inf b/OvmfPkg/Library/PlatformInitLib/PlatformInitLib.inf index d2a0bec434..d2fa2d998d 100644 --- a/OvmfPkg/Library/PlatformInitLib/PlatformInitLib.inf +++ b/OvmfPkg/Library/PlatformInitLib/PlatformInitLib.inf @@ -50,6 +50,7 @@ MtrrLib PcdLib PciLib + PeiHardwareInfoLib [LibraryClasses.X64] TdxLib