X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FPlatformPei%2FPlatform.c;h=e5e8581752b5a430624f65666b2386e693e5a600;hb=83357313dd6750e5c3c4e290676acee9d391d9e3;hp=c48fe974be71bf059f251466109cfe3ea6c0497b;hpb=4b455f7bf0da7ab11a6366a1ab9ac3c8905411a4;p=mirror_edk2.git diff --git a/OvmfPkg/PlatformPei/Platform.c b/OvmfPkg/PlatformPei/Platform.c index c48fe974be..e5e8581752 100644 --- a/OvmfPkg/PlatformPei/Platform.c +++ b/OvmfPkg/PlatformPei/Platform.c @@ -1,16 +1,10 @@ /**@file Platform PEI driver - Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
Copyright (c) 2011, Andrei Warkentin - This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -22,6 +16,7 @@ // // The Library classes this module consumes // +#include #include #include #include @@ -30,10 +25,16 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include +#include +#include #include "Platform.h" #include "Cmos.h" @@ -59,6 +60,14 @@ EFI_PEI_PPI_DESCRIPTOR mPpiBootMode[] = { }; +UINT16 mHostBridgeDevId; + +EFI_BOOT_MODE mBootMode = BOOT_WITH_FULL_CONFIGURATION; + +BOOLEAN mS3Supported = FALSE; + +UINT32 mMaxCpuCount; + VOID AddIoMemoryBaseSizeHob ( EFI_PHYSICAL_ADDRESS MemoryBase, @@ -79,7 +88,8 @@ AddIoMemoryBaseSizeHob ( VOID AddReservedMemoryBaseSizeHob ( EFI_PHYSICAL_ADDRESS MemoryBase, - UINT64 MemorySize + UINT64 MemorySize, + BOOLEAN Cacheable ) { BuildResourceDescriptorHob ( @@ -87,6 +97,12 @@ AddReservedMemoryBaseSizeHob ( EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | + (Cacheable ? + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE : + 0 + ) | EFI_RESOURCE_ATTRIBUTE_TESTED, MemoryBase, MemorySize @@ -134,40 +150,18 @@ AddMemoryRangeHob ( } -VOID -AddUntestedMemoryBaseSizeHob ( - EFI_PHYSICAL_ADDRESS MemoryBase, - UINT64 MemorySize - ) -{ - BuildResourceDescriptorHob ( - EFI_RESOURCE_SYSTEM_MEMORY, - EFI_RESOURCE_ATTRIBUTE_PRESENT | - EFI_RESOURCE_ATTRIBUTE_INITIALIZED | - EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | - EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | - EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | - EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE, - MemoryBase, - MemorySize - ); -} - - -VOID -AddUntestedMemoryRangeHob ( - EFI_PHYSICAL_ADDRESS MemoryBase, - EFI_PHYSICAL_ADDRESS MemoryLimit - ) -{ - AddUntestedMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase)); -} - VOID MemMapInitialization ( VOID ) { + UINT64 PciIoBase; + UINT64 PciIoSize; + RETURN_STATUS PcdStatus; + + PciIoBase = 0xC000; + PciIoSize = 0x4000; + // // Create Memory Type Information HOB // @@ -177,17 +171,6 @@ MemMapInitialization ( sizeof(mDefaultMemoryTypeInformation) ); - // - // Add PCI IO Port space available for PCI resource allocations. - // - BuildResourceDescriptorHob ( - EFI_RESOURCE_IO, - EFI_RESOURCE_ATTRIBUTE_PRESENT | - EFI_RESOURCE_ATTRIBUTE_INITIALIZED, - 0xC000, - 0x4000 - ); - // // Video memory + Legacy BIOS region // @@ -195,7 +178,25 @@ MemMapInitialization ( if (!mXen) { UINT32 TopOfLowRam; + UINT64 PciExBarBase; + UINT32 PciBase; + UINT32 PciSize; + TopOfLowRam = GetSystemMemorySizeBelow4gb (); + PciExBarBase = 0; + if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) { + // + // The MMCONFIG area is expected to fall between the top of low RAM and + // the base of the 32-bit PCI host aperture. + // + PciExBarBase = FixedPcdGet64 (PcdPciExpressBaseAddress); + ASSERT (TopOfLowRam <= PciExBarBase); + ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB); + PciBase = (UINT32)(PciExBarBase + SIZE_256MB); + } else { + ASSERT (TopOfLowRam <= mQemuUc32Base); + PciBase = mQemuUc32Base; + } // // address purpose size @@ -205,74 +206,289 @@ MemMapInitialization ( // 0xFEC00000 IO-APIC 4 KB // 0xFEC01000 gap 1020 KB // 0xFED00000 HPET 1 KB - // 0xFED00400 gap 1023 KB + // 0xFED00400 gap 111 KB + // 0xFED1C000 gap (PIIX4) / RCRB (ICH9) 16 KB + // 0xFED20000 gap 896 KB // 0xFEE00000 LAPIC 1 MB // - AddIoMemoryRangeHob (TopOfLowRam < BASE_2GB ? - BASE_2GB : TopOfLowRam, 0xFC000000); + PciSize = 0xFC000000 - PciBase; + AddIoMemoryBaseSizeHob (PciBase, PciSize); + PcdStatus = PcdSet64S (PcdPciMmio32Base, PciBase); + ASSERT_RETURN_ERROR (PcdStatus); + PcdStatus = PcdSet64S (PcdPciMmio32Size, PciSize); + ASSERT_RETURN_ERROR (PcdStatus); + AddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB); AddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB); + if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) { + AddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB); + // + // Note: there should be an + // + // AddIoMemoryBaseSizeHob (PciExBarBase, SIZE_256MB); + // + // call below, just like the one above for RCBA. However, Linux insists + // that the MMCONFIG area be marked in the E820 or UEFI memory map as + // "reserved memory" -- Linux does not content itself with a simple gap + // in the memory map wherever the MCFG ACPI table points to. + // + // This appears to be a safety measure. The PCI Firmware Specification + // (rev 3.1) says in 4.1.2. "MCFG Table Description": "The resources can + // *optionally* be returned in [...] EFIGetMemoryMap as reserved memory + // [...]". (Emphasis added here.) + // + // Normally we add memory resource descriptor HOBs in + // QemuInitializeRam(), and pre-allocate from those with memory + // allocation HOBs in InitializeRamRegions(). However, the MMCONFIG area + // is most definitely not RAM; so, as an exception, cover it with + // uncacheable reserved memory right here. + // + AddReservedMemoryBaseSizeHob (PciExBarBase, SIZE_256MB, FALSE); + BuildMemoryAllocationHob (PciExBarBase, SIZE_256MB, + EfiReservedMemoryType); + } AddIoMemoryBaseSizeHob (PcdGet32(PcdCpuLocalApicBaseAddress), SIZE_1MB); - } else { - XenPublishRamRegions (); + + // + // On Q35, the IO Port space is available for PCI resource allocations from + // 0x6000 up. + // + if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) { + PciIoBase = 0x6000; + PciIoSize = 0xA000; + ASSERT ((ICH9_PMBASE_VALUE & 0xF000) < PciIoBase); + } } + + // + // Add PCI IO Port space available for PCI resource allocations. + // + BuildResourceDescriptorHob ( + EFI_RESOURCE_IO, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED, + PciIoBase, + PciIoSize + ); + PcdStatus = PcdSet64S (PcdPciIoBase, PciIoBase); + ASSERT_RETURN_ERROR (PcdStatus); + PcdStatus = PcdSet64S (PcdPciIoSize, PciIoSize); + ASSERT_RETURN_ERROR (PcdStatus); } +EFI_STATUS +GetNamedFwCfgBoolean ( + IN CHAR8 *FwCfgFileName, + OUT BOOLEAN *Setting + ) +{ + EFI_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINT8 Value[3]; + + Status = QemuFwCfgFindFile (FwCfgFileName, &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + return Status; + } + if (FwCfgSize > sizeof Value) { + return EFI_BAD_BUFFER_SIZE; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, Value); + + if ((FwCfgSize == 1) || + (FwCfgSize == 2 && Value[1] == '\n') || + (FwCfgSize == 3 && Value[1] == '\r' && Value[2] == '\n')) { + switch (Value[0]) { + case '0': + case 'n': + case 'N': + *Setting = FALSE; + return EFI_SUCCESS; + + case '1': + case 'y': + case 'Y': + *Setting = TRUE; + return EFI_SUCCESS; + + default: + break; + } + } + return EFI_PROTOCOL_ERROR; +} + +#define UPDATE_BOOLEAN_PCD_FROM_FW_CFG(TokenName) \ + do { \ + BOOLEAN Setting; \ + RETURN_STATUS PcdStatus; \ + \ + if (!EFI_ERROR (GetNamedFwCfgBoolean ( \ + "opt/ovmf/" #TokenName, &Setting))) { \ + PcdStatus = PcdSetBoolS (TokenName, Setting); \ + ASSERT_RETURN_ERROR (PcdStatus); \ + } \ + } while (0) + +VOID +NoexecDxeInitialization ( + VOID + ) +{ + UPDATE_BOOLEAN_PCD_FROM_FW_CFG (PcdPropertiesTableEnable); + UPDATE_BOOLEAN_PCD_FROM_FW_CFG (PcdSetNxForStack); +} + +VOID +PciExBarInitialization ( + VOID + ) +{ + union { + UINT64 Uint64; + UINT32 Uint32[2]; + } PciExBarBase; + + // + // We only support the 256MB size for the MMCONFIG area: + // 256 buses * 32 devices * 8 functions * 4096 bytes config space. + // + // The masks used below enforce the Q35 requirements that the MMCONFIG area + // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB. + // + // Note that (b) also ensures that the minimum address width we have + // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice + // for DXE's page tables to cover the MMCONFIG area. + // + PciExBarBase.Uint64 = FixedPcdGet64 (PcdPciExpressBaseAddress); + ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0); + ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0); + + // + // Clear the PCIEXBAREN bit first, before programming the high register. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0); + + // + // Program the high register. Then program the low register, setting the + // MMCONFIG area size and enabling decoding at once. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]); + PciWrite32 ( + DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), + PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN + ); +} VOID MiscInitialization ( VOID ) { + UINTN PmCmd; + UINTN Pmba; + UINT32 PmbaAndVal; + UINT32 PmbaOrVal; + UINTN AcpiCtlReg; + UINT8 AcpiEnBit; + RETURN_STATUS PcdStatus; + // // Disable A20 Mask // IoOr8 (0x92, BIT1); // - // Build the CPU hob with 36-bit addressing and 16-bits of IO space. + // Build the CPU HOB with guest RAM size dependent address width and 16-bits + // of IO space. (Side note: unlike other HOBs, the CPU HOB is needed during + // S3 resume as well, so we build it unconditionally.) + // + BuildCpuHob (mPhysMemAddressWidth, 16); + + // + // Determine platform type and save Host Bridge DID to PCD // - BuildCpuHob (36, 16); + switch (mHostBridgeDevId) { + case INTEL_82441_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA); + PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK; + PmbaOrVal = PIIX4_PMBA_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC); + AcpiEnBit = PIIX4_PMREGMISC_PMIOSE; + break; + case INTEL_Q35_MCH_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE); + PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK; + PmbaOrVal = ICH9_PMBASE_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL); + AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN; + break; + default: + DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", + __FUNCTION__, mHostBridgeDevId)); + ASSERT (FALSE); + return; + } + PcdStatus = PcdSet16S (PcdOvmfHostBridgePciDevId, mHostBridgeDevId); + ASSERT_RETURN_ERROR (PcdStatus); // - // If PMREGMISC/PMIOSE is set, assume the ACPI PMBA has been configured (for - // example by Xen) and skip the setup here. This matches the logic in - // AcpiTimerLibConstructor (). + // If the appropriate IOspace enable bit is set, assume the ACPI PMBA + // has been configured (e.g., by Xen) and skip the setup here. + // This matches the logic in AcpiTimerLibConstructor (). // - if ((PciRead8 (PCI_LIB_ADDRESS (0, 1, 3, 0x80)) & 0x01) == 0) { + if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) { // - // The PEI phase should be exited with fully accessibe PIIX4 IO space: + // The PEI phase should be exited with fully accessibe ACPI PM IO space: // 1. set PMBA // - PciAndThenOr32 ( - PCI_LIB_ADDRESS (0, 1, 3, 0x40), - (UINT32) ~0xFFC0, - PcdGet16 (PcdAcpiPmBaseAddress) - ); + PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal); // // 2. set PCICMD/IOSE // - PciOr8 ( - PCI_LIB_ADDRESS (0, 1, 3, PCI_COMMAND_OFFSET), - EFI_PCI_COMMAND_IO_SPACE + PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE); + + // + // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN) + // + PciOr8 (AcpiCtlReg, AcpiEnBit); + } + + if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) { + // + // Set Root Complex Register Block BAR + // + PciWrite32 ( + POWER_MGMT_REGISTER_Q35 (ICH9_RCBA), + ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN ); // - // 3. set PMREGMISC/PMIOSE + // Set PCI Express Register Range Base Address // - PciOr8 (PCI_LIB_ADDRESS (0, 1, 3, 0x80), 0x01); + PciExBarInitialization (); } } VOID BootModeInitialization ( + VOID ) { - EFI_STATUS Status; + EFI_STATUS Status; - Status = PeiServicesSetBootMode (BOOT_WITH_FULL_CONFIGURATION); + if (CmosRead8 (0xF) == 0xFE) { + mBootMode = BOOT_ON_S3_RESUME; + } + CmosWrite8 (0xF, 0x00); + + Status = PeiServicesSetBootMode (mBootMode); ASSERT_EFI_ERROR (Status); Status = PeiServicesInstallPpi (mPpiBootMode); @@ -285,6 +501,7 @@ ReserveEmuVariableNvStore ( ) { EFI_PHYSICAL_ADDRESS VariableStore; + RETURN_STATUS PcdStatus; // // Allocate storage for NV variables early on so it will be @@ -294,16 +511,16 @@ ReserveEmuVariableNvStore ( // VariableStore = (EFI_PHYSICAL_ADDRESS)(UINTN) - AllocateAlignedRuntimePages ( - EFI_SIZE_TO_PAGES (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize)), - PcdGet32 (PcdFlashNvStorageFtwSpareSize) + AllocateRuntimePages ( + EFI_SIZE_TO_PAGES (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize)) ); DEBUG ((EFI_D_INFO, "Reserved variable store memory: 0x%lX; size: %dkb\n", VariableStore, (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / 1024 )); - PcdSet64 (PcdEmuVariableNvStoreReserved, VariableStore); + PcdStatus = PcdSet64S (PcdEmuVariableNvStoreReserved, VariableStore); + ASSERT_RETURN_ERROR (PcdStatus); } @@ -312,7 +529,7 @@ DebugDumpCmos ( VOID ) { - UINTN Loop; + UINT32 Loop; DEBUG ((EFI_D_INFO, "CMOS:\n")); @@ -328,6 +545,186 @@ DebugDumpCmos ( } +VOID +S3Verification ( + VOID + ) +{ +#if defined (MDE_CPU_X64) + if (FeaturePcdGet (PcdSmmSmramRequire) && mS3Supported) { + DEBUG ((EFI_D_ERROR, + "%a: S3Resume2Pei doesn't support X64 PEI + SMM yet.\n", __FUNCTION__)); + DEBUG ((EFI_D_ERROR, + "%a: Please disable S3 on the QEMU command line (see the README),\n", + __FUNCTION__)); + DEBUG ((EFI_D_ERROR, + "%a: or build OVMF with \"OvmfPkgIa32X64.dsc\".\n", __FUNCTION__)); + ASSERT (FALSE); + CpuDeadLoop (); + } +#endif +} + + +/** + Fetch the boot CPU count and the possible CPU count from QEMU, and expose + them to UefiCpuPkg modules. Set the mMaxCpuCount variable. +**/ +VOID +MaxCpuCountInitialization ( + VOID + ) +{ + UINT16 BootCpuCount; + RETURN_STATUS PcdStatus; + + // + // Try to fetch the boot CPU count. + // + QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount); + BootCpuCount = QemuFwCfgRead16 (); + if (BootCpuCount == 0) { + // + // QEMU doesn't report the boot CPU count. (BootCpuCount == 0) will let + // MpInitLib count APs up to (PcdCpuMaxLogicalProcessorNumber - 1), or + // until PcdCpuApInitTimeOutInMicroSeconds elapses (whichever is reached + // first). + // + DEBUG ((DEBUG_WARN, "%a: boot CPU count unavailable\n", __FUNCTION__)); + mMaxCpuCount = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); + } else { + // + // We will expose BootCpuCount to MpInitLib. MpInitLib will count APs up to + // (BootCpuCount - 1) precisely, regardless of timeout. + // + // Now try to fetch the possible CPU count. + // + UINTN CpuHpBase; + UINT32 CmdData2; + + CpuHpBase = ((mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) ? + ICH9_CPU_HOTPLUG_BASE : PIIX4_CPU_HOTPLUG_BASE); + + // + // If only legacy mode is available in the CPU hotplug register block, or + // the register block is completely missing, then the writes below are + // no-ops. + // + // 1. Switch the hotplug register block to modern mode. + // + IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0); + // + // 2. Select a valid CPU for deterministic reading of + // QEMU_CPUHP_R_CMD_DATA2. + // + // CPU#0 is always valid; it is the always present and non-removable + // BSP. + // + IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0); + // + // 3. Send a command after which QEMU_CPUHP_R_CMD_DATA2 is specified to + // read as zero, and which does not invalidate the selector. (The + // selector may change, but it must not become invalid.) + // + // Send QEMU_CPUHP_CMD_GET_PENDING, as it will prove useful later. + // + IoWrite8 (CpuHpBase + QEMU_CPUHP_W_CMD, QEMU_CPUHP_CMD_GET_PENDING); + // + // 4. Read QEMU_CPUHP_R_CMD_DATA2. + // + // If the register block is entirely missing, then this is an unassigned + // IO read, returning all-bits-one. + // + // If only legacy mode is available, then bit#0 stands for CPU#0 in the + // "CPU present bitmap". CPU#0 is always present. + // + // Otherwise, QEMU_CPUHP_R_CMD_DATA2 is either still reserved (returning + // all-bits-zero), or it is specified to read as zero after the above + // steps. Both cases confirm modern mode. + // + CmdData2 = IoRead32 (CpuHpBase + QEMU_CPUHP_R_CMD_DATA2); + DEBUG ((DEBUG_VERBOSE, "%a: CmdData2=0x%x\n", __FUNCTION__, CmdData2)); + if (CmdData2 != 0) { + // + // QEMU doesn't support the modern CPU hotplug interface. Assume that the + // possible CPU count equals the boot CPU count (precluding hotplug). + // + DEBUG ((DEBUG_WARN, "%a: modern CPU hotplug interface unavailable\n", + __FUNCTION__)); + mMaxCpuCount = BootCpuCount; + } else { + // + // Grab the possible CPU count from the modern CPU hotplug interface. + // + UINT32 Present, Possible, Selected; + + Present = 0; + Possible = 0; + + // + // We've sent QEMU_CPUHP_CMD_GET_PENDING last; this ensures + // QEMU_CPUHP_RW_CMD_DATA can now be read usefully. However, + // QEMU_CPUHP_CMD_GET_PENDING may have selected a CPU with actual pending + // hotplug events; therefore, select CPU#0 forcibly. + // + IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible); + + do { + UINT8 CpuStatus; + + // + // Read the status of the currently selected CPU. This will help with a + // sanity check against "BootCpuCount". + // + CpuStatus = IoRead8 (CpuHpBase + QEMU_CPUHP_R_CPU_STAT); + if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) != 0) { + ++Present; + } + // + // Attempt to select the next CPU. + // + ++Possible; + IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible); + // + // If the selection is successful, then the following read will return + // the selector (which we know is positive at this point). Otherwise, + // the read will return 0. + // + Selected = IoRead32 (CpuHpBase + QEMU_CPUHP_RW_CMD_DATA); + ASSERT (Selected == Possible || Selected == 0); + } while (Selected > 0); + + // + // Sanity check: fw_cfg and the modern CPU hotplug interface should + // return the same boot CPU count. + // + if (BootCpuCount != Present) { + DEBUG ((DEBUG_WARN, "%a: QEMU v2.7 reset bug: BootCpuCount=%d " + "Present=%u\n", __FUNCTION__, BootCpuCount, Present)); + // + // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus + // platform reset (including S3), was corrected in QEMU commit + // e3cadac073a9 ("pc: fix FW_CFG_NB_CPUS to account for -device added + // CPUs", 2016-11-16), part of release v2.8.0. + // + BootCpuCount = (UINT16)Present; + } + + mMaxCpuCount = Possible; + } + } + + DEBUG ((DEBUG_INFO, "%a: BootCpuCount=%d mMaxCpuCount=%u\n", __FUNCTION__, + BootCpuCount, mMaxCpuCount)); + ASSERT (BootCpuCount <= mMaxCpuCount); + + PcdStatus = PcdSet32S (PcdCpuBootLogicalProcessorNumber, BootCpuCount); + ASSERT_RETURN_ERROR (PcdStatus); + PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, mMaxCpuCount); + ASSERT_RETURN_ERROR (PcdStatus); +} + + /** Perform Platform PEI initialization. @@ -344,32 +741,60 @@ InitializePlatform ( IN CONST EFI_PEI_SERVICES **PeiServices ) { - DEBUG ((EFI_D_ERROR, "Platform PEIM Loaded\n")); + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n")); DebugDumpCmos (); XenDetect (); + if (QemuFwCfgS3Enabled ()) { + DEBUG ((EFI_D_INFO, "S3 support was detected on QEMU\n")); + mS3Supported = TRUE; + Status = PcdSetBoolS (PcdAcpiS3Enable, TRUE); + ASSERT_EFI_ERROR (Status); + } + + S3Verification (); BootModeInitialization (); + AddressWidthInitialization (); - PublishPeiMemory (); + // + // Query Host Bridge DID + // + mHostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID); - if (!mXen) { - MemDetect (); + MaxCpuCountInitialization (); + + if (FeaturePcdGet (PcdSmmSmramRequire)) { + Q35TsegMbytesInitialization (); } + PublishPeiMemory (); + + QemuUc32BaseInitialization (); + + InitializeRamRegions (); + if (mXen) { DEBUG ((EFI_D_INFO, "Xen was detected\n")); InitializeXen (); } - ReserveEmuVariableNvStore (); - - PeiFvInitialization (); - - MemMapInitialization (); + if (mBootMode != BOOT_ON_S3_RESUME) { + if (!FeaturePcdGet (PcdSmmSmramRequire)) { + ReserveEmuVariableNvStore (); + } + PeiFvInitialization (); + MemMapInitialization (); + NoexecDxeInitialization (); + } + InstallClearCacheCallback (); + AmdSevInitialize (); MiscInitialization (); + InstallFeatureControlCallback (); return EFI_SUCCESS; }