diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/OvmfPkg/PlatformPei | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/OvmfPkg/PlatformPei')
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/AmdSev.c | 195 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/ClearCache.c | 111 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Cmos.c | 58 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Cmos.h | 50 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/FeatureControl.c | 128 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Fv.c | 94 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/MemDetect.c | 964 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/MemTypeInfo.c | 215 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Platform.c | 759 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Platform.h | 136 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/PlatformPei.inf | 135 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Xen.c | 222 | ||||
-rw-r--r-- | roms/edk2/OvmfPkg/PlatformPei/Xen.h | 39 |
13 files changed, 3106 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/PlatformPei/AmdSev.c b/roms/edk2/OvmfPkg/PlatformPei/AmdSev.c new file mode 100644 index 000000000..4a515a484 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/AmdSev.c @@ -0,0 +1,195 @@ +/**@file
+ Initialize Secure Encrypted Virtualization (SEV) support
+
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+//
+// The package level header files this module uses
+//
+#include <IndustryStandard/Q35MchIch9.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <PiPei.h>
+#include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Cpuid.h>
+#include <Register/Intel/SmramSaveStateMap.h>
+
+#include "Platform.h"
+
+/**
+
+ Initialize SEV-ES support if running as an SEV-ES guest.
+
+ **/
+STATIC
+VOID
+AmdSevEsInitialize (
+ VOID
+ )
+{
+ VOID *GhcbBase;
+ PHYSICAL_ADDRESS GhcbBasePa;
+ UINTN GhcbPageCount, PageCount;
+ RETURN_STATUS PcdStatus, DecryptStatus;
+ IA32_DESCRIPTOR Gdtr;
+ VOID *Gdt;
+
+ if (!MemEncryptSevEsIsEnabled ()) {
+ return;
+ }
+
+ PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ //
+ // Allocate GHCB and per-CPU variable pages.
+ // Since the pages must survive across the UEFI to OS transition
+ // make them reserved.
+ //
+ GhcbPageCount = mMaxCpuCount * 2;
+ GhcbBase = AllocateReservedPages (GhcbPageCount);
+ ASSERT (GhcbBase != NULL);
+
+ GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
+
+ //
+ // Each vCPU gets two consecutive pages, the first is the GHCB and the
+ // second is the per-CPU variable page. Loop through the allocation and
+ // only clear the encryption mask for the GHCB pages.
+ //
+ for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
+ DecryptStatus = MemEncryptSevClearPageEncMask (
+ 0,
+ GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
+ 1,
+ TRUE
+ );
+ ASSERT_RETURN_ERROR (DecryptStatus);
+ }
+
+ ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
+
+ PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ DEBUG ((DEBUG_INFO,
+ "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
+ (UINT64)GhcbPageCount, GhcbBase));
+
+ AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
+
+ //
+ // The SEV support will clear the C-bit from non-RAM areas. The early GDT
+ // lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
+ // will be read as un-encrypted even though it was created before the C-bit
+ // was cleared (encrypted). This will result in a failure to be able to
+ // handle the exception.
+ //
+ AsmReadGdtr (&Gdtr);
+
+ Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN) Gdtr.Limit + 1));
+ ASSERT (Gdt != NULL);
+
+ CopyMem (Gdt, (VOID *) Gdtr.Base, Gdtr.Limit + 1);
+ Gdtr.Base = (UINTN) Gdt;
+ AsmWriteGdtr (&Gdtr);
+}
+
+/**
+
+ Function checks if SEV support is available, if present then it sets
+ the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.
+
+ **/
+VOID
+AmdSevInitialize (
+ VOID
+ )
+{
+ CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx;
+ UINT64 EncryptionMask;
+ RETURN_STATUS PcdStatus;
+
+ //
+ // Check if SEV is enabled
+ //
+ if (!MemEncryptSevIsEnabled ()) {
+ return;
+ }
+
+ //
+ // CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position)
+ //
+ AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL);
+ EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits);
+
+ //
+ // Set Memory Encryption Mask PCD
+ //
+ PcdStatus = PcdSet64S (PcdPteMemoryEncryptionAddressOrMask, EncryptionMask);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ DEBUG ((DEBUG_INFO, "SEV is enabled (mask 0x%lx)\n", EncryptionMask));
+
+ //
+ // Set Pcd to Deny the execution of option ROM when security
+ // violation.
+ //
+ PcdStatus = PcdSet32S (PcdOptionRomImageVerificationPolicy, 0x4);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ //
+ // When SMM is required, cover the pages containing the initial SMRAM Save
+ // State Map with a memory allocation HOB:
+ //
+ // There's going to be a time interval between our decrypting those pages for
+ // SMBASE relocation and re-encrypting the same pages after SMBASE
+ // relocation. We shall ensure that the DXE phase stay away from those pages
+ // until after re-encryption, in order to prevent an information leak to the
+ // hypervisor.
+ //
+ if (FeaturePcdGet (PcdSmmSmramRequire) && (mBootMode != BOOT_ON_S3_RESUME)) {
+ RETURN_STATUS LocateMapStatus;
+ UINTN MapPagesBase;
+ UINTN MapPagesCount;
+
+ LocateMapStatus = MemEncryptSevLocateInitialSmramSaveStateMapPages (
+ &MapPagesBase,
+ &MapPagesCount
+ );
+ ASSERT_RETURN_ERROR (LocateMapStatus);
+
+ if (mQ35SmramAtDefaultSmbase) {
+ //
+ // The initial SMRAM Save State Map has been covered as part of a larger
+ // reserved memory allocation in InitializeRamRegions().
+ //
+ ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);
+ ASSERT (
+ (MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=
+ SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)
+ );
+ } else {
+ BuildMemoryAllocationHob (
+ MapPagesBase, // BaseAddress
+ EFI_PAGES_TO_SIZE (MapPagesCount), // Length
+ EfiBootServicesData // MemoryType
+ );
+ }
+ }
+
+ //
+ // Check and perform SEV-ES initialization if required.
+ //
+ AmdSevEsInitialize ();
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/ClearCache.c b/roms/edk2/OvmfPkg/PlatformPei/ClearCache.c new file mode 100644 index 000000000..5c538c59e --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/ClearCache.c @@ -0,0 +1,111 @@ +/**@file
+ Install a callback to clear cache on all processors.
+ This is for conformance with the TCG "Platform Reset Attack Mitigation
+ Specification". Because clearing the CPU caches at boot doesn't impact
+ performance significantly, do it unconditionally, for simplicity's
+ sake.
+
+ Copyright (C) 2018, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Ppi/MpServices.h>
+
+#include "Platform.h"
+
+/**
+ Invalidate data & instruction caches.
+ All APs execute this function in parallel. The BSP executes the function
+ separately.
+
+ @param[in,out] WorkSpace Pointer to the input/output argument workspace
+ shared by all processors.
+**/
+STATIC
+VOID
+EFIAPI
+ClearCache (
+ IN OUT VOID *WorkSpace
+ )
+{
+ WriteBackInvalidateDataCache ();
+ InvalidateInstructionCache ();
+}
+
+/**
+ Notification function called when EFI_PEI_MP_SERVICES_PPI becomes available.
+
+ @param[in] PeiServices Indirect reference to the PEI Services Table.
+ @param[in] NotifyDescriptor Address of the notification descriptor data
+ structure.
+ @param[in] Ppi Address of the PPI that was installed.
+
+ @return Status of the notification. The status code returned from this
+ function is ignored.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ClearCacheOnMpServicesAvailable (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ EFI_PEI_MP_SERVICES_PPI *MpServices;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "%a: %a\n", gEfiCallerBaseName, __FUNCTION__));
+
+ //
+ // Clear cache on all the APs in parallel.
+ //
+ MpServices = Ppi;
+ Status = MpServices->StartupAllAPs (
+ (CONST EFI_PEI_SERVICES **)PeiServices,
+ MpServices,
+ ClearCache, // Procedure
+ FALSE, // SingleThread
+ 0, // TimeoutInMicroSeconds: inf.
+ NULL // ProcedureArgument
+ );
+ if (EFI_ERROR (Status) && Status != EFI_NOT_STARTED) {
+ DEBUG ((DEBUG_ERROR, "%a: StartupAllAps(): %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Now clear cache on the BSP too.
+ //
+ ClearCache (NULL);
+ return EFI_SUCCESS;
+}
+
+//
+// Notification object for registering the callback, for when
+// EFI_PEI_MP_SERVICES_PPI becomes available.
+//
+STATIC CONST EFI_PEI_NOTIFY_DESCRIPTOR mMpServicesNotify = {
+ EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | // Flags
+ EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
+ &gEfiPeiMpServicesPpiGuid, // Guid
+ ClearCacheOnMpServicesAvailable // Notify
+};
+
+VOID
+InstallClearCacheCallback (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PeiServicesNotifyPpi (&mMpServicesNotify);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to set up MP Services callback: %r\n",
+ __FUNCTION__, Status));
+ }
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Cmos.c b/roms/edk2/OvmfPkg/PlatformPei/Cmos.c new file mode 100644 index 000000000..9b34e10b1 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Cmos.c @@ -0,0 +1,58 @@ +/** @file
+ PC/AT CMOS access routines
+
+ Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Cmos.h"
+#include "Library/IoLib.h"
+
+/**
+ Reads 8-bits of CMOS data.
+
+ Reads the 8-bits of CMOS data at the location specified by Index.
+ The 8-bit read value is returned.
+
+ @param Index The CMOS location to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+CmosRead8 (
+ IN UINTN Index
+ )
+{
+ IoWrite8 (0x70, (UINT8) Index);
+ return IoRead8 (0x71);
+}
+
+
+/**
+ Writes 8-bits of CMOS data.
+
+ Writes 8-bits of CMOS data to the location specified by Index
+ with the value specified by Value and returns Value.
+
+ @param Index The CMOS location to write.
+ @param Value The value to write to CMOS.
+
+ @return The value written to CMOS.
+
+**/
+UINT8
+EFIAPI
+CmosWrite8 (
+ IN UINTN Index,
+ IN UINT8 Value
+ )
+{
+ IoWrite8 (0x70, (UINT8) Index);
+ IoWrite8 (0x71, Value);
+ return Value;
+}
+
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Cmos.h b/roms/edk2/OvmfPkg/PlatformPei/Cmos.h new file mode 100644 index 000000000..3cd98799a --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Cmos.h @@ -0,0 +1,50 @@ +/** @file
+ PC/AT CMOS access routines
+
+ Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __CMOS_H__
+#define __CMOS_H__
+
+/**
+ Reads 8-bits of CMOS data.
+
+ Reads the 8-bits of CMOS data at the location specified by Index.
+ The 8-bit read value is returned.
+
+ @param Index The CMOS location to read.
+
+ @return The value read.
+
+**/
+UINT8
+EFIAPI
+CmosRead8 (
+ IN UINTN Index
+ );
+
+/**
+ Writes 8-bits of CMOS data.
+
+ Writes 8-bits of CMOS data to the location specified by Index
+ with the value specified by Value and returns Value.
+
+ @param Index The CMOS location to write.
+ @param Value The value to write to CMOS.
+
+ @return The value written to CMOS.
+
+**/
+UINT8
+EFIAPI
+CmosWrite8 (
+ IN UINTN Index,
+ IN UINT8 Value
+ );
+
+
+#endif
+
diff --git a/roms/edk2/OvmfPkg/PlatformPei/FeatureControl.c b/roms/edk2/OvmfPkg/PlatformPei/FeatureControl.c new file mode 100644 index 000000000..dccf9505d --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/FeatureControl.c @@ -0,0 +1,128 @@ +/**@file
+ Install a callback when necessary for setting the Feature Control MSR on all
+ processors.
+
+ Copyright (C) 2016, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Ppi/MpServices.h>
+#include <Register/ArchitecturalMsr.h>
+
+#include "Platform.h"
+
+//
+// The value to be written to the Feature Control MSR, retrieved from fw_cfg.
+//
+STATIC UINT64 mFeatureControlValue;
+
+/**
+ Write the Feature Control MSR on an Application Processor or the Boot
+ Processor.
+
+ All APs execute this function in parallel. The BSP executes the function
+ separately.
+
+ @param[in,out] WorkSpace Pointer to the input/output argument workspace
+ shared by all processors.
+**/
+STATIC
+VOID
+EFIAPI
+WriteFeatureControl (
+ IN OUT VOID *WorkSpace
+ )
+{
+ AsmWriteMsr64 (MSR_IA32_FEATURE_CONTROL, mFeatureControlValue);
+}
+
+/**
+ Notification function called when EFI_PEI_MP_SERVICES_PPI becomes available.
+
+ @param[in] PeiServices Indirect reference to the PEI Services Table.
+ @param[in] NotifyDescriptor Address of the notification descriptor data
+ structure.
+ @param[in] Ppi Address of the PPI that was installed.
+
+ @return Status of the notification. The status code returned from this
+ function is ignored.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+OnMpServicesAvailable (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ EFI_PEI_MP_SERVICES_PPI *MpServices;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: %a\n", gEfiCallerBaseName, __FUNCTION__));
+
+ //
+ // Write the MSR on all the APs in parallel.
+ //
+ MpServices = Ppi;
+ Status = MpServices->StartupAllAPs (
+ (CONST EFI_PEI_SERVICES **)PeiServices,
+ MpServices,
+ WriteFeatureControl, // Procedure
+ FALSE, // SingleThread
+ 0, // TimeoutInMicroSeconds: inf.
+ NULL // ProcedureArgument
+ );
+ if (EFI_ERROR (Status) && Status != EFI_NOT_STARTED) {
+ DEBUG ((DEBUG_ERROR, "%a: StartupAllAps(): %r\n", __FUNCTION__, Status));
+ return Status;
+ }
+
+ //
+ // Now write the MSR on the BSP too.
+ //
+ WriteFeatureControl (NULL);
+ return EFI_SUCCESS;
+}
+
+//
+// Notification object for registering the callback, for when
+// EFI_PEI_MP_SERVICES_PPI becomes available.
+//
+STATIC CONST EFI_PEI_NOTIFY_DESCRIPTOR mMpServicesNotify = {
+ EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | // Flags
+ EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
+ &gEfiPeiMpServicesPpiGuid, // Guid
+ OnMpServicesAvailable // Notify
+};
+
+VOID
+InstallFeatureControlCallback (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+
+ Status = QemuFwCfgFindFile ("etc/msr_feature_control", &FwCfgItem,
+ &FwCfgSize);
+ if (EFI_ERROR (Status) || FwCfgSize != sizeof mFeatureControlValue) {
+ //
+ // Nothing to do.
+ //
+ return;
+ }
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (sizeof mFeatureControlValue, &mFeatureControlValue);
+
+ Status = PeiServicesNotifyPpi (&mMpServicesNotify);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to set up MP Services callback: %r\n",
+ __FUNCTION__, Status));
+ }
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Fv.c b/roms/edk2/OvmfPkg/PlatformPei/Fv.c new file mode 100644 index 000000000..ee4ecab61 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Fv.c @@ -0,0 +1,94 @@ +/** @file
+ Build FV related hobs for platform.
+
+ Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PiPei.h"
+#include "Platform.h"
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+
+
+/**
+ Publish PEI & DXE (Decompressed) Memory based FVs to let PEI
+ and DXE know about them.
+
+ @retval EFI_SUCCESS Platform PEI FVs were initialized successfully.
+
+**/
+EFI_STATUS
+PeiFvInitialization (
+ VOID
+ )
+{
+ BOOLEAN SecureS3Needed;
+
+ DEBUG ((DEBUG_INFO, "Platform PEI Firmware Volume Initialization\n"));
+
+ //
+ // Create a memory allocation HOB for the PEI FV.
+ //
+ // Allocate as ACPI NVS is S3 is supported
+ //
+ BuildMemoryAllocationHob (
+ PcdGet32 (PcdOvmfPeiMemFvBase),
+ PcdGet32 (PcdOvmfPeiMemFvSize),
+ mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
+ );
+
+ //
+ // Let DXE know about the DXE FV
+ //
+ BuildFvHob (PcdGet32 (PcdOvmfDxeMemFvBase), PcdGet32 (PcdOvmfDxeMemFvSize));
+
+ SecureS3Needed = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire);
+
+ //
+ // Create a memory allocation HOB for the DXE FV.
+ //
+ // If "secure" S3 is needed, then SEC will decompress both PEI and DXE
+ // firmware volumes at S3 resume too, hence we need to keep away the OS from
+ // DXEFV as well. Otherwise we only need to keep away DXE itself from the
+ // DXEFV area.
+ //
+ BuildMemoryAllocationHob (
+ PcdGet32 (PcdOvmfDxeMemFvBase),
+ PcdGet32 (PcdOvmfDxeMemFvSize),
+ SecureS3Needed ? EfiACPIMemoryNVS : EfiBootServicesData
+ );
+
+ //
+ // Additionally, said decompression will use temporary memory above the end
+ // of DXEFV, so let's keep away the OS from there too.
+ //
+ if (SecureS3Needed) {
+ UINT32 DxeMemFvEnd;
+
+ DxeMemFvEnd = PcdGet32 (PcdOvmfDxeMemFvBase) +
+ PcdGet32 (PcdOvmfDxeMemFvSize);
+ BuildMemoryAllocationHob (
+ DxeMemFvEnd,
+ PcdGet32 (PcdOvmfDecompressionScratchEnd) - DxeMemFvEnd,
+ EfiACPIMemoryNVS
+ );
+ }
+
+ //
+ // Let PEI know about the DXE FV so it can find the DXE Core
+ //
+ PeiServicesInstallFvInfoPpi (
+ NULL,
+ (VOID *)(UINTN) PcdGet32 (PcdOvmfDxeMemFvBase),
+ PcdGet32 (PcdOvmfDxeMemFvSize),
+ NULL,
+ NULL
+ );
+
+ return EFI_SUCCESS;
+}
+
diff --git a/roms/edk2/OvmfPkg/PlatformPei/MemDetect.c b/roms/edk2/OvmfPkg/PlatformPei/MemDetect.c new file mode 100644 index 000000000..ffbbef891 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/MemDetect.c @@ -0,0 +1,964 @@ +/**@file
+ Memory Detection for Virtual Machines.
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+Module Name:
+
+ MemDetect.c
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <IndustryStandard/E820.h>
+#include <IndustryStandard/I440FxPiix4.h>
+#include <IndustryStandard/Q35MchIch9.h>
+#include <PiPei.h>
+#include <Register/Intel/SmramSaveStateMap.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Library/MtrrLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgSimpleParserLib.h>
+
+#include "Platform.h"
+#include "Cmos.h"
+
+UINT8 mPhysMemAddressWidth;
+
+STATIC UINT32 mS3AcpiReservedMemoryBase;
+STATIC UINT32 mS3AcpiReservedMemorySize;
+
+STATIC UINT16 mQ35TsegMbytes;
+
+BOOLEAN mQ35SmramAtDefaultSmbase;
+
+UINT32 mQemuUc32Base;
+
+VOID
+Q35TsegMbytesInitialization (
+ VOID
+ )
+{
+ UINT16 ExtendedTsegMbytes;
+ RETURN_STATUS PcdStatus;
+
+ ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
+
+ //
+ // Check if QEMU offers an extended TSEG.
+ //
+ // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
+ // register, and reading back the register.
+ //
+ // On a QEMU machine type that does not offer an extended TSEG, the initial
+ // write overwrites whatever value a malicious guest OS may have placed in
+ // the (unimplemented) register, before entering S3 or rebooting.
+ // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
+ //
+ // On a QEMU machine type that offers an extended TSEG, the initial write
+ // triggers an update to the register. Subsequently, the value read back
+ // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
+ // number of megabytes.
+ //
+ PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);
+ ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));
+ if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {
+ mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
+ return;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: QEMU offers an extended TSEG (%d MB)\n",
+ __FUNCTION__,
+ ExtendedTsegMbytes
+ ));
+ PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ mQ35TsegMbytes = ExtendedTsegMbytes;
+}
+
+
+VOID
+Q35SmramAtDefaultSmbaseInitialization (
+ VOID
+ )
+{
+ RETURN_STATUS PcdStatus;
+
+ ASSERT (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
+
+ mQ35SmramAtDefaultSmbase = FALSE;
+ if (FeaturePcdGet (PcdCsmEnable)) {
+ DEBUG ((DEBUG_INFO, "%a: SMRAM at default SMBASE not checked due to CSM\n",
+ __FUNCTION__));
+ } else {
+ UINTN CtlReg;
+ UINT8 CtlRegVal;
+
+ CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);
+ PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);
+ CtlRegVal = PciRead8 (CtlReg);
+ mQ35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==
+ MCH_DEFAULT_SMBASE_IN_RAM);
+ DEBUG ((DEBUG_INFO, "%a: SMRAM at default SMBASE %a\n", __FUNCTION__,
+ mQ35SmramAtDefaultSmbase ? "found" : "not found"));
+ }
+
+ PcdStatus = PcdSetBoolS (PcdQ35SmramAtDefaultSmbase,
+ mQ35SmramAtDefaultSmbase);
+ ASSERT_RETURN_ERROR (PcdStatus);
+}
+
+
+VOID
+QemuUc32BaseInitialization (
+ VOID
+ )
+{
+ UINT32 LowerMemorySize;
+ UINT32 Uc32Size;
+
+ if (mXen) {
+ return;
+ }
+
+ if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
+ //
+ // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
+ // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
+ // setting PcdPciExpressBaseAddress such that describing the
+ // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
+ // variable MTRRs (preferably 1 or 2).
+ //
+ ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
+ mQemuUc32Base = (UINT32)FixedPcdGet64 (PcdPciExpressBaseAddress);
+ return;
+ }
+
+ ASSERT (mHostBridgeDevId == INTEL_82441_DEVICE_ID);
+ //
+ // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
+ // variable MTRR suffices by truncating the size to a whole power of two,
+ // while keeping the end affixed to 4GB. This will round the base up.
+ //
+ LowerMemorySize = GetSystemMemorySizeBelow4gb ();
+ Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));
+ mQemuUc32Base = (UINT32)(SIZE_4GB - Uc32Size);
+ //
+ // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
+ // Therefore mQemuUc32Base is at least 2GB.
+ //
+ ASSERT (mQemuUc32Base >= BASE_2GB);
+
+ if (mQemuUc32Base != LowerMemorySize) {
+ DEBUG ((DEBUG_VERBOSE, "%a: rounded UC32 base from 0x%x up to 0x%x, for "
+ "an UC32 size of 0x%x\n", __FUNCTION__, LowerMemorySize, mQemuUc32Base,
+ Uc32Size));
+ }
+}
+
+
+/**
+ Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
+ of the 32-bit address range.
+
+ Find the highest exclusive >=4GB RAM address, or produce memory resource
+ descriptor HOBs for RAM entries that start at or above 4GB.
+
+ @param[out] MaxAddress If MaxAddress is NULL, then ScanOrAdd64BitE820Ram()
+ produces memory resource descriptor HOBs for RAM
+ entries that start at or above 4GB.
+
+ Otherwise, MaxAddress holds the highest exclusive
+ >=4GB RAM address on output. If QEMU's fw_cfg E820
+ RAM map contains no RAM entry that starts outside of
+ the 32-bit address range, then MaxAddress is exactly
+ 4GB on output.
+
+ @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
+
+ @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
+ whole multiple of sizeof(EFI_E820_ENTRY64). No
+ RAM entry was processed.
+
+ @return Error codes from QemuFwCfgFindFile(). No RAM
+ entry was processed.
+**/
+STATIC
+EFI_STATUS
+ScanOrAdd64BitE820Ram (
+ OUT UINT64 *MaxAddress OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ EFI_E820_ENTRY64 E820Entry;
+ UINTN Processed;
+
+ Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (FwCfgSize % sizeof E820Entry != 0) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (MaxAddress != NULL) {
+ *MaxAddress = BASE_4GB;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
+ QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
+ __FUNCTION__,
+ E820Entry.BaseAddr,
+ E820Entry.Length,
+ E820Entry.Type
+ ));
+ if (E820Entry.Type == EfiAcpiAddressRangeMemory &&
+ E820Entry.BaseAddr >= BASE_4GB) {
+ if (MaxAddress == NULL) {
+ UINT64 Base;
+ UINT64 End;
+
+ //
+ // Round up the start address, and round down the end address.
+ //
+ Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);
+ End = (E820Entry.BaseAddr + E820Entry.Length) &
+ ~(UINT64)EFI_PAGE_MASK;
+ if (Base < End) {
+ AddMemoryRangeHob (Base, End);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: AddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
+ __FUNCTION__,
+ Base,
+ End
+ ));
+ }
+ } else {
+ UINT64 Candidate;
+
+ Candidate = E820Entry.BaseAddr + E820Entry.Length;
+ if (Candidate > *MaxAddress) {
+ *MaxAddress = Candidate;
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: MaxAddress=0x%Lx\n",
+ __FUNCTION__,
+ *MaxAddress
+ ));
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+
+UINT32
+GetSystemMemorySizeBelow4gb (
+ VOID
+ )
+{
+ UINT8 Cmos0x34;
+ UINT8 Cmos0x35;
+
+ //
+ // CMOS 0x34/0x35 specifies the system memory above 16 MB.
+ // * CMOS(0x35) is the high byte
+ // * CMOS(0x34) is the low byte
+ // * The size is specified in 64kb chunks
+ // * Since this is memory above 16MB, the 16MB must be added
+ // into the calculation to get the total memory size.
+ //
+
+ Cmos0x34 = (UINT8) CmosRead8 (0x34);
+ Cmos0x35 = (UINT8) CmosRead8 (0x35);
+
+ return (UINT32) (((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
+}
+
+
+STATIC
+UINT64
+GetSystemMemorySizeAbove4gb (
+ )
+{
+ UINT32 Size;
+ UINTN CmosIndex;
+
+ //
+ // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
+ // * CMOS(0x5d) is the most significant size byte
+ // * CMOS(0x5c) is the middle size byte
+ // * CMOS(0x5b) is the least significant size byte
+ // * The size is specified in 64kb chunks
+ //
+
+ Size = 0;
+ for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
+ Size = (UINT32) (Size << 8) + (UINT32) CmosRead8 (CmosIndex);
+ }
+
+ return LShiftU64 (Size, 16);
+}
+
+
+/**
+ Return the highest address that DXE could possibly use, plus one.
+**/
+STATIC
+UINT64
+GetFirstNonAddress (
+ VOID
+ )
+{
+ UINT64 FirstNonAddress;
+ UINT64 Pci64Base, Pci64Size;
+ UINT32 FwCfgPciMmio64Mb;
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ UINT64 HotPlugMemoryEnd;
+ RETURN_STATUS PcdStatus;
+
+ //
+ // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
+ //
+ FirstNonAddress = 0;
+
+ //
+ // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
+ // address from it. This can express an address >= 4GB+1TB.
+ //
+ // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
+ // can only express a size smaller than 1TB), and add it to 4GB.
+ //
+ Status = ScanOrAdd64BitE820Ram (&FirstNonAddress);
+ if (EFI_ERROR (Status)) {
+ FirstNonAddress = BASE_4GB + GetSystemMemorySizeAbove4gb ();
+ }
+
+ //
+ // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
+ // resources to 32-bit anyway. See DegradeResource() in
+ // "PciResourceSupport.c".
+ //
+#ifdef MDE_CPU_IA32
+ if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+ return FirstNonAddress;
+ }
+#endif
+
+ //
+ // Otherwise, in order to calculate the highest address plus one, we must
+ // consider the 64-bit PCI host aperture too. Fetch the default size.
+ //
+ Pci64Size = PcdGet64 (PcdPciMmio64Size);
+
+ //
+ // See if the user specified the number of megabytes for the 64-bit PCI host
+ // aperture. Accept an aperture size up to 16TB.
+ //
+ // As signaled by the "X-" prefix, this knob is experimental, and might go
+ // away at any time.
+ //
+ Status = QemuFwCfgParseUint32 ("opt/ovmf/X-PciMmio64Mb", FALSE,
+ &FwCfgPciMmio64Mb);
+ switch (Status) {
+ case EFI_UNSUPPORTED:
+ case EFI_NOT_FOUND:
+ break;
+ case EFI_SUCCESS:
+ if (FwCfgPciMmio64Mb <= 0x1000000) {
+ Pci64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
+ break;
+ }
+ //
+ // fall through
+ //
+ default:
+ DEBUG ((DEBUG_WARN,
+ "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
+ __FUNCTION__));
+ break;
+ }
+
+ if (Pci64Size == 0) {
+ if (mBootMode != BOOT_ON_S3_RESUME) {
+ DEBUG ((DEBUG_INFO, "%a: disabling 64-bit PCI host aperture\n",
+ __FUNCTION__));
+ PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ }
+
+ //
+ // There's nothing more to do; the amount of memory above 4GB fully
+ // determines the highest address plus one. The memory hotplug area (see
+ // below) plays no role for the firmware in this case.
+ //
+ return FirstNonAddress;
+ }
+
+ //
+ // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
+ // absolute, exclusive end address for the memory hotplug area. This area
+ // starts right at the end of the memory above 4GB. The 64-bit PCI host
+ // aperture must be placed above it.
+ //
+ Status = QemuFwCfgFindFile ("etc/reserved-memory-end", &FwCfgItem,
+ &FwCfgSize);
+ if (!EFI_ERROR (Status) && FwCfgSize == sizeof HotPlugMemoryEnd) {
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
+ DEBUG ((DEBUG_VERBOSE, "%a: HotPlugMemoryEnd=0x%Lx\n", __FUNCTION__,
+ HotPlugMemoryEnd));
+
+ ASSERT (HotPlugMemoryEnd >= FirstNonAddress);
+ FirstNonAddress = HotPlugMemoryEnd;
+ }
+
+ //
+ // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
+ // that the host can map it with 1GB hugepages. Follow suit.
+ //
+ Pci64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);
+ Pci64Size = ALIGN_VALUE (Pci64Size, (UINT64)SIZE_1GB);
+
+ //
+ // The 64-bit PCI host aperture should also be "naturally" aligned. The
+ // alignment is determined by rounding the size of the aperture down to the
+ // next smaller or equal power of two. That is, align the aperture by the
+ // largest BAR size that can fit into it.
+ //
+ Pci64Base = ALIGN_VALUE (Pci64Base, GetPowerOfTwo64 (Pci64Size));
+
+ if (mBootMode != BOOT_ON_S3_RESUME) {
+ //
+ // The core PciHostBridgeDxe driver will automatically add this range to
+ // the GCD memory space map through our PciHostBridgeLib instance; here we
+ // only need to set the PCDs.
+ //
+ PcdStatus = PcdSet64S (PcdPciMmio64Base, Pci64Base);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ PcdStatus = PcdSet64S (PcdPciMmio64Size, Pci64Size);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ DEBUG ((DEBUG_INFO, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
+ __FUNCTION__, Pci64Base, Pci64Size));
+ }
+
+ //
+ // The useful address space ends with the 64-bit PCI host aperture.
+ //
+ FirstNonAddress = Pci64Base + Pci64Size;
+ return FirstNonAddress;
+}
+
+
+/**
+ Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
+**/
+VOID
+AddressWidthInitialization (
+ VOID
+ )
+{
+ UINT64 FirstNonAddress;
+
+ //
+ // 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 = GetFirstNonAddress ();
+ mPhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);
+
+ //
+ // If FirstNonAddress is not an integral power of two, then we need an
+ // additional bit.
+ //
+ if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {
+ ++mPhysMemAddressWidth;
+ }
+
+ //
+ // The minimum address width is 36 (covers up to and excluding 64 GB, which
+ // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
+ // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
+ // can simply assert that here, since 48 bits are good enough for 256 TB.
+ //
+ if (mPhysMemAddressWidth <= 36) {
+ mPhysMemAddressWidth = 36;
+ }
+ ASSERT (mPhysMemAddressWidth <= 48);
+}
+
+
+/**
+ Calculate the cap for the permanent PEI memory.
+**/
+STATIC
+UINT32
+GetPeiMemoryCap (
+ VOID
+ )
+{
+ BOOLEAN Page1GSupport;
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ UINT32 Pml4Entries;
+ UINT32 PdpEntries;
+ UINTN TotalPages;
+
+ //
+ // If DXE is 32-bit, then just return the traditional 64 MB cap.
+ //
+#ifdef MDE_CPU_IA32
+ if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+ return SIZE_64MB;
+ }
+#endif
+
+ //
+ // Dependent on physical address width, PEI memory allocations can be
+ // dominated by the page tables built for 64-bit DXE. So we key the cap off
+ // of those. The code below is based on CreateIdentityMappingPageTables() in
+ // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
+ //
+ Page1GSupport = FALSE;
+ if (PcdGetBool (PcdUse1GPageTable)) {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ if ((RegEdx & BIT26) != 0) {
+ Page1GSupport = TRUE;
+ }
+ }
+ }
+
+ if (mPhysMemAddressWidth <= 39) {
+ Pml4Entries = 1;
+ PdpEntries = 1 << (mPhysMemAddressWidth - 30);
+ ASSERT (PdpEntries <= 0x200);
+ } else {
+ Pml4Entries = 1 << (mPhysMemAddressWidth - 39);
+ ASSERT (Pml4Entries <= 0x200);
+ PdpEntries = 512;
+ }
+
+ TotalPages = Page1GSupport ? Pml4Entries + 1 :
+ (PdpEntries + 1) * Pml4Entries + 1;
+ ASSERT (TotalPages <= 0x40201);
+
+ //
+ // Add 64 MB for miscellaneous allocations. Note that for
+ // mPhysMemAddressWidth values close to 36, the cap will actually be
+ // dominated by this increment.
+ //
+ return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
+}
+
+
+/**
+ Publish PEI core memory
+
+ @return EFI_SUCCESS The PEIM initialized successfully.
+
+**/
+EFI_STATUS
+PublishPeiMemory (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS MemoryBase;
+ UINT64 MemorySize;
+ UINT32 LowerMemorySize;
+ UINT32 PeiMemoryCap;
+
+ LowerMemorySize = GetSystemMemorySizeBelow4gb ();
+ if (FeaturePcdGet (PcdSmmSmramRequire)) {
+ //
+ // TSEG is chipped from the end of low RAM
+ //
+ LowerMemorySize -= mQ35TsegMbytes * SIZE_1MB;
+ }
+
+ //
+ // If S3 is supported, then the S3 permanent PEI memory is placed next,
+ // downwards. Its size is primarily dictated by CpuMpPei. The formula below
+ // is an approximation.
+ //
+ if (mS3Supported) {
+ mS3AcpiReservedMemorySize = SIZE_512KB +
+ mMaxCpuCount *
+ PcdGet32 (PcdCpuApStackSize);
+ mS3AcpiReservedMemoryBase = LowerMemorySize - mS3AcpiReservedMemorySize;
+ LowerMemorySize = mS3AcpiReservedMemoryBase;
+ }
+
+ if (mBootMode == BOOT_ON_S3_RESUME) {
+ MemoryBase = mS3AcpiReservedMemoryBase;
+ MemorySize = mS3AcpiReservedMemorySize;
+ } else {
+ PeiMemoryCap = GetPeiMemoryCap ();
+ DEBUG ((DEBUG_INFO, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
+ __FUNCTION__, mPhysMemAddressWidth, PeiMemoryCap >> 10));
+
+ //
+ // Determine the range of memory to use during PEI
+ //
+ // Technically we could lay the permanent PEI RAM over SEC's temporary
+ // decompression and scratch buffer even if "secure S3" is needed, since
+ // their lifetimes don't overlap. However, PeiFvInitialization() will cover
+ // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
+ // allocation HOB, and other allocations served from the permanent PEI RAM
+ // shouldn't overlap with that HOB.
+ //
+ MemoryBase = mS3Supported && FeaturePcdGet (PcdSmmSmramRequire) ?
+ PcdGet32 (PcdOvmfDecompressionScratchEnd) :
+ PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
+ MemorySize = LowerMemorySize - MemoryBase;
+ if (MemorySize > PeiMemoryCap) {
+ MemoryBase = LowerMemorySize - PeiMemoryCap;
+ MemorySize = PeiMemoryCap;
+ }
+ }
+
+ //
+ // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
+ // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
+ // permanent PEI RAM is located even higher.
+ //
+ if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {
+ ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
+ }
+
+ //
+ // Publish this memory to the PEI Core
+ //
+ Status = PublishSystemMemory(MemoryBase, MemorySize);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+
+STATIC
+VOID
+QemuInitializeRamBelow1gb (
+ VOID
+ )
+{
+ if (FeaturePcdGet (PcdSmmSmramRequire) && mQ35SmramAtDefaultSmbase) {
+ AddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
+ AddReservedMemoryBaseSizeHob (SMM_DEFAULT_SMBASE, MCH_DEFAULT_SMBASE_SIZE,
+ TRUE /* Cacheable */);
+ STATIC_ASSERT (
+ SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
+ "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
+ );
+ AddMemoryRangeHob (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
+ BASE_512KB + BASE_128KB);
+ } else {
+ AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
+ }
+}
+
+
+/**
+ Peform Memory Detection for QEMU / KVM
+
+**/
+STATIC
+VOID
+QemuInitializeRam (
+ VOID
+ )
+{
+ UINT64 LowerMemorySize;
+ UINT64 UpperMemorySize;
+ MTRR_SETTINGS MtrrSettings;
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));
+
+ //
+ // Determine total memory size available
+ //
+ LowerMemorySize = GetSystemMemorySizeBelow4gb ();
+ UpperMemorySize = GetSystemMemorySizeAbove4gb ();
+
+ if (mBootMode == BOOT_ON_S3_RESUME) {
+ //
+ // Create the following memory HOB as an exception on the S3 boot path.
+ //
+ // Normally we'd create memory HOBs only on the normal boot path. However,
+ // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
+ // well, for "borrowing" a subset of it temporarily, for the AP startup
+ // vector.
+ //
+ // CpuMpPei saves the original contents of the borrowed area in permanent
+ // PEI RAM, in a backup buffer allocated with the normal PEI services.
+ // CpuMpPei restores the original contents ("returns" the borrowed area) at
+ // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
+ // transferring control to the OS's wakeup vector in the FACS.
+ //
+ // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
+ // restore the original contents. Furthermore, we expect all such PEIMs
+ // (CpuMpPei included) to claim the borrowed areas by producing memory
+ // allocation HOBs, and to honor preexistent memory allocation HOBs when
+ // looking for an area to borrow.
+ //
+ QemuInitializeRamBelow1gb ();
+ } else {
+ //
+ // Create memory HOBs
+ //
+ QemuInitializeRamBelow1gb ();
+
+ if (FeaturePcdGet (PcdSmmSmramRequire)) {
+ UINT32 TsegSize;
+
+ TsegSize = mQ35TsegMbytes * SIZE_1MB;
+ AddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);
+ AddReservedMemoryBaseSizeHob (LowerMemorySize - TsegSize, TsegSize,
+ TRUE);
+ } else {
+ AddMemoryRangeHob (BASE_1MB, LowerMemorySize);
+ }
+
+ //
+ // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
+ // entries. Otherwise, create a single memory HOB with the flat >=4GB
+ // memory size read from the CMOS.
+ //
+ Status = ScanOrAdd64BitE820Ram (NULL);
+ if (EFI_ERROR (Status) && UpperMemorySize != 0) {
+ AddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
+ }
+ }
+
+ //
+ // We'd like to keep the following ranges uncached:
+ // - [640 KB, 1 MB)
+ // - [LowerMemorySize, 4 GB)
+ //
+ // Everything else should be WB. Unfortunately, programming the inverse (ie.
+ // keeping the default UC, and configuring the complement set of the above as
+ // WB) is not reliable in general, because the end of the upper RAM can have
+ // practically any alignment, and we may not have enough variable MTRRs to
+ // cover it exactly.
+ //
+ if (IsMtrrSupported ()) {
+ MtrrGetAllMtrrs (&MtrrSettings);
+
+ //
+ // MTRRs disabled, fixed MTRRs disabled, default type is uncached
+ //
+ ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
+ ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
+ ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
+
+ //
+ // flip default type to writeback
+ //
+ SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
+ ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
+ MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
+ MtrrSetAllMtrrs (&MtrrSettings);
+
+ //
+ // Set memory range from 640KB to 1MB to uncacheable
+ //
+ Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,
+ BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
+ // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
+ //
+ Status = MtrrSetMemoryAttribute (mQemuUc32Base, SIZE_4GB - mQemuUc32Base,
+ CacheUncacheable);
+ ASSERT_EFI_ERROR (Status);
+ }
+}
+
+/**
+ Publish system RAM and reserve memory regions
+
+**/
+VOID
+InitializeRamRegions (
+ VOID
+ )
+{
+ if (!mXen) {
+ QemuInitializeRam ();
+ } else {
+ XenPublishRamRegions ();
+ }
+
+ if (mS3Supported && mBootMode != BOOT_ON_S3_RESUME) {
+ //
+ // This is the memory range that will be used for PEI on S3 resume
+ //
+ BuildMemoryAllocationHob (
+ mS3AcpiReservedMemoryBase,
+ mS3AcpiReservedMemorySize,
+ EfiACPIMemoryNVS
+ );
+
+ //
+ // Cover the initial RAM area used as stack and temporary PEI heap.
+ //
+ // This is reserved as ACPI NVS so it can be used on S3 resume.
+ //
+ BuildMemoryAllocationHob (
+ PcdGet32 (PcdOvmfSecPeiTempRamBase),
+ PcdGet32 (PcdOvmfSecPeiTempRamSize),
+ EfiACPIMemoryNVS
+ );
+
+ //
+ // SEC stores its table of GUIDed section handlers here.
+ //
+ BuildMemoryAllocationHob (
+ PcdGet64 (PcdGuidedExtractHandlerTableAddress),
+ PcdGet32 (PcdGuidedExtractHandlerTableSize),
+ EfiACPIMemoryNVS
+ );
+
+#ifdef MDE_CPU_X64
+ //
+ // Reserve the initial page tables built by the reset vector code.
+ //
+ // Since this memory range will be used by the Reset Vector on S3
+ // resume, it must be reserved as ACPI NVS.
+ //
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecPageTablesBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),
+ EfiACPIMemoryNVS
+ );
+
+ if (MemEncryptSevEsIsEnabled ()) {
+ //
+ // If SEV-ES is enabled, reserve the GHCB-related memory area. This
+ // includes the extra page table used to break down the 2MB page
+ // mapping into 4KB page entries where the GHCB resides and the
+ // GHCB area itself.
+ //
+ // Since this memory range will be used by the Reset Vector on S3
+ // resume, it must be reserved as ACPI NVS.
+ //
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableSize),
+ EfiACPIMemoryNVS
+ );
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbSize),
+ EfiACPIMemoryNVS
+ );
+ }
+#endif
+ }
+
+ if (mBootMode != BOOT_ON_S3_RESUME) {
+ if (!FeaturePcdGet (PcdSmmSmramRequire)) {
+ //
+ // Reserve the lock box storage area
+ //
+ // Since this memory range will be used on S3 resume, it must be
+ // reserved as ACPI NVS.
+ //
+ // If S3 is unsupported, then various drivers might still write to the
+ // LockBox area. We ought to prevent DXE from serving allocation requests
+ // such that they would overlap the LockBox storage.
+ //
+ ZeroMem (
+ (VOID*)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),
+ (UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize)
+ );
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfLockBoxStorageSize),
+ mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
+ );
+ }
+
+ if (FeaturePcdGet (PcdSmmSmramRequire)) {
+ UINT32 TsegSize;
+
+ //
+ // Make sure the TSEG area that we reported as a reserved memory resource
+ // cannot be used for reserved memory allocations.
+ //
+ TsegSize = mQ35TsegMbytes * SIZE_1MB;
+ BuildMemoryAllocationHob (
+ GetSystemMemorySizeBelow4gb() - TsegSize,
+ TsegSize,
+ EfiReservedMemoryType
+ );
+ //
+ // Similarly, allocate away the (already reserved) SMRAM at the default
+ // SMBASE, if it exists.
+ //
+ if (mQ35SmramAtDefaultSmbase) {
+ BuildMemoryAllocationHob (
+ SMM_DEFAULT_SMBASE,
+ MCH_DEFAULT_SMBASE_SIZE,
+ EfiReservedMemoryType
+ );
+ }
+ }
+
+#ifdef MDE_CPU_X64
+ if (MemEncryptSevEsIsEnabled ()) {
+ //
+ // If SEV-ES is enabled, reserve the SEV-ES work area.
+ //
+ // Since this memory range will be used by the Reset Vector on S3
+ // resume, it must be reserved as ACPI NVS.
+ //
+ // If S3 is unsupported, then various drivers might still write to the
+ // work area. We ought to prevent DXE from serving allocation requests
+ // such that they would overlap the work area.
+ //
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaBase),
+ (UINT64)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaSize),
+ mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
+ );
+ }
+#endif
+ }
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/MemTypeInfo.c b/roms/edk2/OvmfPkg/PlatformPei/MemTypeInfo.c new file mode 100644 index 000000000..f3ce2b686 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/MemTypeInfo.c @@ -0,0 +1,215 @@ +/** @file
+ Produce the memory type information HOB.
+
+ Copyright (C) 2017-2020, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Guid/MemoryTypeInformation.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Ppi/ReadOnlyVariable2.h>
+#include <Uefi/UefiMultiPhase.h>
+
+#include "Platform.h"
+
+#define MEMORY_TYPE_INFO_DEFAULT(Type) \
+ { Type, FixedPcdGet32 (PcdMemoryType ## Type) }
+
+STATIC EFI_MEMORY_TYPE_INFORMATION mMemoryTypeInformation[] = {
+ MEMORY_TYPE_INFO_DEFAULT (EfiACPIMemoryNVS),
+ MEMORY_TYPE_INFO_DEFAULT (EfiACPIReclaimMemory),
+ MEMORY_TYPE_INFO_DEFAULT (EfiReservedMemoryType),
+ MEMORY_TYPE_INFO_DEFAULT (EfiRuntimeServicesCode),
+ MEMORY_TYPE_INFO_DEFAULT (EfiRuntimeServicesData),
+ { EfiMaxMemoryType, 0 }
+};
+
+STATIC
+VOID
+BuildMemTypeInfoHob (
+ VOID
+ )
+{
+ BuildGuidDataHob (
+ &gEfiMemoryTypeInformationGuid,
+ mMemoryTypeInformation,
+ sizeof mMemoryTypeInformation
+ );
+}
+
+/**
+ Refresh the mMemoryTypeInformation array (which we'll turn into the
+ MemoryTypeInformation HOB) from the MemoryTypeInformation UEFI variable.
+
+ Normally, the DXE IPL PEIM builds the HOB from the UEFI variable. But it does
+ so *transparently*. Instead, we consider the UEFI variable as a list of
+ hints, for updating our HOB defaults:
+
+ - Record types not covered in mMemoryTypeInformation are ignored. In
+ particular, this hides record types from the UEFI variable that may lead to
+ reboots without benefiting SMM security, such as EfiBootServicesData.
+
+ - Records that would lower the defaults in mMemoryTypeInformation are also
+ ignored.
+
+ @param[in] ReadOnlyVariable2 The EFI_PEI_READ_ONLY_VARIABLE2_PPI used for
+ retrieving the MemoryTypeInformation UEFI
+ variable.
+**/
+STATIC
+VOID
+RefreshMemTypeInfo (
+ IN EFI_PEI_READ_ONLY_VARIABLE2_PPI *ReadOnlyVariable2
+ )
+{
+ UINTN DataSize;
+ EFI_MEMORY_TYPE_INFORMATION Entries[EfiMaxMemoryType + 1];
+ EFI_STATUS Status;
+ UINTN NumEntries;
+ UINTN HobRecordIdx;
+
+ //
+ // Read the MemoryTypeInformation UEFI variable from the
+ // gEfiMemoryTypeInformationGuid namespace.
+ //
+ DataSize = sizeof Entries;
+ Status = ReadOnlyVariable2->GetVariable (
+ ReadOnlyVariable2,
+ EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
+ &gEfiMemoryTypeInformationGuid,
+ NULL,
+ &DataSize,
+ Entries
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If the UEFI variable does not exist (EFI_NOT_FOUND), we can't use it for
+ // udpating mMemoryTypeInformation.
+ //
+ // If the UEFI variable exists but Entries is too small to hold it
+ // (EFI_BUFFER_TOO_SMALL), then the variable contents are arguably invalid.
+ // That's because Entries has room for every distinct EFI_MEMORY_TYPE,
+ // including the terminator record with EfiMaxMemoryType. Thus, we can't
+ // use the UEFI variable for updating mMemoryTypeInformation.
+ //
+ // If the UEFI variable couldn't be read for some other reason, we
+ // similarly can't use it for udpating mMemoryTypeInformation.
+ //
+ DEBUG ((DEBUG_ERROR, "%a: GetVariable(): %r\n", __FUNCTION__, Status));
+ return;
+ }
+
+ //
+ // Sanity-check the UEFI variable size against the record size.
+ //
+ if (DataSize % sizeof Entries[0] != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid UEFI variable size %Lu\n", __FUNCTION__,
+ (UINT64)DataSize));
+ return;
+ }
+ NumEntries = DataSize / sizeof Entries[0];
+
+ //
+ // For each record in mMemoryTypeInformation, except the terminator record,
+ // look up the first match (if any) in the UEFI variable, based on the memory
+ // type.
+ //
+ for (HobRecordIdx = 0;
+ HobRecordIdx < ARRAY_SIZE (mMemoryTypeInformation) - 1;
+ HobRecordIdx++) {
+ EFI_MEMORY_TYPE_INFORMATION *HobRecord;
+ UINTN Idx;
+ EFI_MEMORY_TYPE_INFORMATION *VariableRecord;
+
+ HobRecord = &mMemoryTypeInformation[HobRecordIdx];
+
+ for (Idx = 0; Idx < NumEntries; Idx++) {
+ VariableRecord = &Entries[Idx];
+
+ if (VariableRecord->Type == HobRecord->Type) {
+ break;
+ }
+ }
+
+ //
+ // If there is a match, allow the UEFI variable to increase NumberOfPages.
+ //
+ if (Idx < NumEntries &&
+ HobRecord->NumberOfPages < VariableRecord->NumberOfPages) {
+ DEBUG ((DEBUG_VERBOSE, "%a: Type 0x%x: NumberOfPages 0x%x -> 0x%x\n",
+ __FUNCTION__, HobRecord->Type, HobRecord->NumberOfPages,
+ VariableRecord->NumberOfPages));
+
+ HobRecord->NumberOfPages = VariableRecord->NumberOfPages;
+ }
+ }
+}
+
+/**
+ Notification function called when EFI_PEI_READ_ONLY_VARIABLE2_PPI becomes
+ available.
+
+ @param[in] PeiServices Indirect reference to the PEI Services Table.
+ @param[in] NotifyDescriptor Address of the notification descriptor data
+ structure.
+ @param[in] Ppi Address of the PPI that was installed.
+
+ @return Status of the notification. The status code returned from this
+ function is ignored.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+OnReadOnlyVariable2Available (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
+
+ RefreshMemTypeInfo (Ppi);
+ BuildMemTypeInfoHob ();
+ return EFI_SUCCESS;
+}
+
+//
+// Notification object for registering the callback, for when
+// EFI_PEI_READ_ONLY_VARIABLE2_PPI becomes available.
+//
+STATIC CONST EFI_PEI_NOTIFY_DESCRIPTOR mReadOnlyVariable2Notify = {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH |
+ EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), // Flags
+ &gEfiPeiReadOnlyVariable2PpiGuid, // Guid
+ OnReadOnlyVariable2Available // Notify
+};
+
+VOID
+MemTypeInfoInitialization (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ if (!FeaturePcdGet (PcdSmmSmramRequire)) {
+ //
+ // EFI_PEI_READ_ONLY_VARIABLE2_PPI will never be available; install
+ // the default memory type information HOB right away.
+ //
+ BuildMemTypeInfoHob ();
+ return;
+ }
+
+ Status = PeiServicesNotifyPpi (&mReadOnlyVariable2Notify);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to set up R/O Variable 2 callback: %r\n",
+ __FUNCTION__, Status));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Platform.c b/roms/edk2/OvmfPkg/PlatformPei/Platform.c new file mode 100644 index 000000000..96468701e --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Platform.c @@ -0,0 +1,759 @@ +/**@file
+ Platform PEI driver
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Library/QemuFwCfgSimpleParserLib.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Ppi/MasterBootMode.h>
+#include <IndustryStandard/I440FxPiix4.h>
+#include <IndustryStandard/Pci22.h>
+#include <IndustryStandard/Q35MchIch9.h>
+#include <IndustryStandard/QemuCpuHotplug.h>
+#include <OvmfPlatforms.h>
+
+#include "Platform.h"
+#include "Cmos.h"
+
+EFI_PEI_PPI_DESCRIPTOR mPpiBootMode[] = {
+ {
+ EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
+ &gEfiPeiMasterBootModePpiGuid,
+ NULL
+ }
+};
+
+
+UINT16 mHostBridgeDevId;
+
+EFI_BOOT_MODE mBootMode = BOOT_WITH_FULL_CONFIGURATION;
+
+BOOLEAN mS3Supported = FALSE;
+
+UINT32 mMaxCpuCount;
+
+VOID
+AddIoMemoryBaseSizeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ UINT64 MemorySize
+ )
+{
+ BuildResourceDescriptorHob (
+ EFI_RESOURCE_MEMORY_MAPPED_IO,
+ EFI_RESOURCE_ATTRIBUTE_PRESENT |
+ EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+ EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_TESTED,
+ MemoryBase,
+ MemorySize
+ );
+}
+
+VOID
+AddReservedMemoryBaseSizeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ UINT64 MemorySize,
+ BOOLEAN Cacheable
+ )
+{
+ BuildResourceDescriptorHob (
+ EFI_RESOURCE_MEMORY_RESERVED,
+ 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
+ );
+}
+
+VOID
+AddIoMemoryRangeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ EFI_PHYSICAL_ADDRESS MemoryLimit
+ )
+{
+ AddIoMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));
+}
+
+
+VOID
+AddMemoryBaseSizeHob (
+ 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 |
+ EFI_RESOURCE_ATTRIBUTE_TESTED,
+ MemoryBase,
+ MemorySize
+ );
+}
+
+
+VOID
+AddMemoryRangeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ EFI_PHYSICAL_ADDRESS MemoryLimit
+ )
+{
+ AddMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));
+}
+
+
+VOID
+MemMapInitialization (
+ VOID
+ )
+{
+ UINT64 PciIoBase;
+ UINT64 PciIoSize;
+ RETURN_STATUS PcdStatus;
+
+ PciIoBase = 0xC000;
+ PciIoSize = 0x4000;
+
+ //
+ // Video memory + Legacy BIOS region
+ //
+ AddIoMemoryRangeHob (0x0A0000, BASE_1MB);
+
+ 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
+ // ------------ -------- -------------------------
+ // max(top, 2g) PCI MMIO 0xFC000000 - max(top, 2g)
+ // 0xFC000000 gap 44 MB
+ // 0xFEC00000 IO-APIC 4 KB
+ // 0xFEC01000 gap 1020 KB
+ // 0xFED00000 HPET 1 KB
+ // 0xFED00400 gap 111 KB
+ // 0xFED1C000 gap (PIIX4) / RCRB (ICH9) 16 KB
+ // 0xFED20000 gap 896 KB
+ // 0xFEE00000 LAPIC 1 MB
+ //
+ 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);
+
+ //
+ // 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);
+}
+
+#define UPDATE_BOOLEAN_PCD_FROM_FW_CFG(TokenName) \
+ do { \
+ BOOLEAN Setting; \
+ RETURN_STATUS PcdStatus; \
+ \
+ if (!RETURN_ERROR (QemuFwCfgParseBool ( \
+ "opt/ovmf/" #TokenName, &Setting))) { \
+ PcdStatus = PcdSetBoolS (TokenName, Setting); \
+ ASSERT_RETURN_ERROR (PcdStatus); \
+ } \
+ } while (0)
+
+VOID
+NoexecDxeInitialization (
+ VOID
+ )
+{
+ 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 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
+ //
+ 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 ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
+ __FUNCTION__, mHostBridgeDevId));
+ ASSERT (FALSE);
+ return;
+ }
+ PcdStatus = PcdSet16S (PcdOvmfHostBridgePciDevId, mHostBridgeDevId);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ //
+ // 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 (AcpiCtlReg) & AcpiEnBit) == 0) {
+ //
+ // The PEI phase should be exited with fully accessibe ACPI PM IO space:
+ // 1. set PMBA
+ //
+ PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);
+
+ //
+ // 2. set PCICMD/IOSE
+ //
+ 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
+ );
+
+ //
+ // Set PCI Express Register Range Base Address
+ //
+ PciExBarInitialization ();
+ }
+}
+
+
+VOID
+BootModeInitialization (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ if (CmosRead8 (0xF) == 0xFE) {
+ mBootMode = BOOT_ON_S3_RESUME;
+ }
+ CmosWrite8 (0xF, 0x00);
+
+ Status = PeiServicesSetBootMode (mBootMode);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PeiServicesInstallPpi (mPpiBootMode);
+ ASSERT_EFI_ERROR (Status);
+}
+
+
+VOID
+ReserveEmuVariableNvStore (
+ )
+{
+ EFI_PHYSICAL_ADDRESS VariableStore;
+ RETURN_STATUS PcdStatus;
+
+ //
+ // Allocate storage for NV variables early on so it will be
+ // at a consistent address. Since VM memory is preserved
+ // across reboots, this allows the NV variable storage to survive
+ // a VM reboot.
+ //
+ VariableStore =
+ (EFI_PHYSICAL_ADDRESS)(UINTN)
+ AllocateRuntimePages (
+ EFI_SIZE_TO_PAGES (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize))
+ );
+ DEBUG ((DEBUG_INFO,
+ "Reserved variable store memory: 0x%lX; size: %dkb\n",
+ VariableStore,
+ (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / 1024
+ ));
+ PcdStatus = PcdSet64S (PcdEmuVariableNvStoreReserved, VariableStore);
+ ASSERT_RETURN_ERROR (PcdStatus);
+}
+
+
+VOID
+DebugDumpCmos (
+ VOID
+ )
+{
+ UINT32 Loop;
+
+ DEBUG ((DEBUG_INFO, "CMOS:\n"));
+
+ for (Loop = 0; Loop < 0x80; Loop++) {
+ if ((Loop % 0x10) == 0) {
+ DEBUG ((DEBUG_INFO, "%02x:", Loop));
+ }
+ DEBUG ((DEBUG_INFO, " %02x", CmosRead8 (Loop)));
+ if ((Loop % 0x10) == 0xf) {
+ DEBUG ((DEBUG_INFO, "\n"));
+ }
+ }
+}
+
+
+VOID
+S3Verification (
+ VOID
+ )
+{
+#if defined (MDE_CPU_X64)
+ if (FeaturePcdGet (PcdSmmSmramRequire) && mS3Supported) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: S3Resume2Pei doesn't support X64 PEI + SMM yet.\n", __FUNCTION__));
+ DEBUG ((DEBUG_ERROR,
+ "%a: Please disable S3 on the QEMU command line (see the README),\n",
+ __FUNCTION__));
+ DEBUG ((DEBUG_ERROR,
+ "%a: or build OVMF with \"OvmfPkgIa32X64.dsc\".\n", __FUNCTION__));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+#endif
+}
+
+
+VOID
+Q35BoardVerification (
+ VOID
+ )
+{
+ if (mHostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
+ return;
+ }
+
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "
+ "only DID=0x%04x (Q35) is supported\n",
+ __FUNCTION__,
+ mHostBridgeDevId,
+ INTEL_Q35_MCH_DEVICE_ID
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
+
+
+/**
+ 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.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @return EFI_SUCCESS The PEIM initialized successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializePlatform (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n"));
+
+ DebugDumpCmos ();
+
+ XenDetect ();
+
+ if (QemuFwCfgS3Enabled ()) {
+ DEBUG ((DEBUG_INFO, "S3 support was detected on QEMU\n"));
+ mS3Supported = TRUE;
+ Status = PcdSetBoolS (PcdAcpiS3Enable, TRUE);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ S3Verification ();
+ BootModeInitialization ();
+ AddressWidthInitialization ();
+
+ //
+ // Query Host Bridge DID
+ //
+ mHostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+
+ MaxCpuCountInitialization ();
+
+ if (FeaturePcdGet (PcdSmmSmramRequire)) {
+ Q35BoardVerification ();
+ Q35TsegMbytesInitialization ();
+ Q35SmramAtDefaultSmbaseInitialization ();
+ }
+
+ PublishPeiMemory ();
+
+ QemuUc32BaseInitialization ();
+
+ InitializeRamRegions ();
+
+ if (mXen) {
+ DEBUG ((DEBUG_INFO, "Xen was detected\n"));
+ InitializeXen ();
+ }
+
+ if (mBootMode != BOOT_ON_S3_RESUME) {
+ if (!FeaturePcdGet (PcdSmmSmramRequire)) {
+ ReserveEmuVariableNvStore ();
+ }
+ PeiFvInitialization ();
+ MemTypeInfoInitialization ();
+ MemMapInitialization ();
+ NoexecDxeInitialization ();
+ }
+
+ InstallClearCacheCallback ();
+ AmdSevInitialize ();
+ MiscInitialization ();
+ InstallFeatureControlCallback ();
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Platform.h b/roms/edk2/OvmfPkg/PlatformPei/Platform.h new file mode 100644 index 000000000..0484ec9e6 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Platform.h @@ -0,0 +1,136 @@ +/** @file
+ Platform PEI module include file.
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _PLATFORM_PEI_H_INCLUDED_
+#define _PLATFORM_PEI_H_INCLUDED_
+
+#include <IndustryStandard/E820.h>
+
+VOID
+AddIoMemoryBaseSizeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ UINT64 MemorySize
+ );
+
+VOID
+AddIoMemoryRangeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ EFI_PHYSICAL_ADDRESS MemoryLimit
+ );
+
+VOID
+AddMemoryBaseSizeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ UINT64 MemorySize
+ );
+
+VOID
+AddMemoryRangeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ EFI_PHYSICAL_ADDRESS MemoryLimit
+ );
+
+VOID
+AddReservedMemoryBaseSizeHob (
+ EFI_PHYSICAL_ADDRESS MemoryBase,
+ UINT64 MemorySize,
+ BOOLEAN Cacheable
+ );
+
+VOID
+AddressWidthInitialization (
+ VOID
+ );
+
+VOID
+Q35TsegMbytesInitialization (
+ VOID
+ );
+
+VOID
+Q35SmramAtDefaultSmbaseInitialization (
+ VOID
+ );
+
+EFI_STATUS
+PublishPeiMemory (
+ VOID
+ );
+
+UINT32
+GetSystemMemorySizeBelow4gb (
+ VOID
+ );
+
+VOID
+QemuUc32BaseInitialization (
+ VOID
+ );
+
+VOID
+InitializeRamRegions (
+ VOID
+ );
+
+EFI_STATUS
+PeiFvInitialization (
+ VOID
+ );
+
+VOID
+MemTypeInfoInitialization (
+ VOID
+ );
+
+VOID
+InstallFeatureControlCallback (
+ VOID
+ );
+
+VOID
+InstallClearCacheCallback (
+ VOID
+ );
+
+EFI_STATUS
+InitializeXen (
+ VOID
+ );
+
+BOOLEAN
+XenDetect (
+ VOID
+ );
+
+VOID
+AmdSevInitialize (
+ VOID
+ );
+
+extern BOOLEAN mXen;
+
+VOID
+XenPublishRamRegions (
+ VOID
+ );
+
+extern EFI_BOOT_MODE mBootMode;
+
+extern BOOLEAN mS3Supported;
+
+extern UINT8 mPhysMemAddressWidth;
+
+extern UINT32 mMaxCpuCount;
+
+extern UINT16 mHostBridgeDevId;
+
+extern BOOLEAN mQ35SmramAtDefaultSmbase;
+
+extern UINT32 mQemuUc32Base;
+
+#endif // _PLATFORM_PEI_H_INCLUDED_
diff --git a/roms/edk2/OvmfPkg/PlatformPei/PlatformPei.inf b/roms/edk2/OvmfPkg/PlatformPei/PlatformPei.inf new file mode 100644 index 000000000..c53be2f49 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/PlatformPei.inf @@ -0,0 +1,135 @@ +## @file
+# Platform PEI driver
+#
+# This module provides platform specific function to detect boot mode.
+# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PlatformPei
+ FILE_GUID = 222c386d-5abc-4fb4-b124-fbb82488acf4
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+ ENTRY_POINT = InitializePlatform
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ AmdSev.c
+ ClearCache.c
+ Cmos.c
+ Cmos.h
+ FeatureControl.c
+ Fv.c
+ MemDetect.c
+ MemTypeInfo.c
+ Platform.c
+ Platform.h
+ Xen.c
+ Xen.h
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[Guids]
+ gEfiMemoryTypeInformationGuid
+ gEfiXenInfoGuid
+
+[LibraryClasses]
+ BaseLib
+ CacheMaintenanceLib
+ DebugLib
+ HobLib
+ IoLib
+ PciLib
+ ResourcePublicationLib
+ PeiServicesLib
+ PeiServicesTablePointerLib
+ PeimEntryPoint
+ QemuFwCfgLib
+ QemuFwCfgS3Lib
+ QemuFwCfgSimpleParserLib
+ MtrrLib
+ MemEncryptSevLib
+ PcdLib
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base
+ gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd
+ gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes
+ gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase
+ gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize
+ gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled
+
+[FixedPcd]
+ gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
+ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiACPIMemoryNVS
+ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiACPIReclaimMemory
+ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType
+ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode
+ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdCsmEnable
+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
+
+[Ppis]
+ gEfiPeiMasterBootModePpiGuid
+ gEfiPeiMpServicesPpiGuid
+ gEfiPeiReadOnlyVariable2PpiGuid
+
+[Depex]
+ TRUE
+
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Xen.c b/roms/edk2/OvmfPkg/PlatformPei/Xen.c new file mode 100644 index 000000000..104922c67 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Xen.c @@ -0,0 +1,222 @@ +/**@file
+ Xen Platform PEI support
+
+ Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Guid/XenInfo.h>
+#include <IndustryStandard/E820.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Library/MtrrLib.h>
+
+#include "Platform.h"
+#include "Xen.h"
+
+BOOLEAN mXen = FALSE;
+
+STATIC UINT32 mXenLeaf = 0;
+
+EFI_XEN_INFO mXenInfo;
+
+/**
+ Returns E820 map provided by Xen
+
+ @param Entries Pointer to E820 map
+ @param Count Number of entries
+
+ @return EFI_STATUS
+**/
+EFI_STATUS
+XenGetE820Map (
+ EFI_E820_ENTRY64 **Entries,
+ UINT32 *Count
+ )
+{
+ EFI_XEN_OVMF_INFO *Info =
+ (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;
+
+ if (AsciiStrCmp ((CHAR8 *) Info->Signature, "XenHVMOVMF")) {
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT (Info->E820 < MAX_ADDRESS);
+ *Entries = (EFI_E820_ENTRY64 *)(UINTN) Info->E820;
+ *Count = Info->E820EntriesCount;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Connects to the Hypervisor.
+
+ @param XenLeaf CPUID index used to connect.
+
+ @return EFI_STATUS
+
+**/
+EFI_STATUS
+XenConnect (
+ UINT32 XenLeaf
+ )
+{
+ UINT32 Index;
+ UINT32 TransferReg;
+ UINT32 TransferPages;
+ UINT32 XenVersion;
+
+ AsmCpuid (XenLeaf + 2, &TransferPages, &TransferReg, NULL, NULL);
+ mXenInfo.HyperPages = AllocatePages (TransferPages);
+ if (!mXenInfo.HyperPages) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 0; Index < TransferPages; Index++) {
+ AsmWriteMsr64 (TransferReg,
+ (UINTN) mXenInfo.HyperPages +
+ (Index << EFI_PAGE_SHIFT) + Index);
+ }
+
+ AsmCpuid (XenLeaf + 1, &XenVersion, NULL, NULL, NULL);
+ DEBUG ((DEBUG_ERROR, "Detected Xen version %d.%d\n",
+ XenVersion >> 16, XenVersion & 0xFFFF));
+ mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);
+ mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);
+
+ BuildGuidDataHob (
+ &gEfiXenInfoGuid,
+ &mXenInfo,
+ sizeof(mXenInfo)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Figures out if we are running inside Xen HVM.
+
+ @retval TRUE Xen was detected
+ @retval FALSE Xen was not detected
+
+**/
+BOOLEAN
+XenDetect (
+ VOID
+ )
+{
+ UINT8 Signature[13];
+
+ if (mXenLeaf != 0) {
+ return TRUE;
+ }
+
+ Signature[12] = '\0';
+ for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {
+ AsmCpuid (mXenLeaf,
+ NULL,
+ (UINT32 *) &Signature[0],
+ (UINT32 *) &Signature[4],
+ (UINT32 *) &Signature[8]);
+
+ if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {
+ mXen = TRUE;
+ return TRUE;
+ }
+ }
+
+ mXenLeaf = 0;
+ return FALSE;
+}
+
+
+VOID
+XenPublishRamRegions (
+ VOID
+ )
+{
+ EFI_E820_ENTRY64 *E820Map;
+ UINT32 E820EntriesCount;
+ EFI_STATUS Status;
+
+ if (!mXen) {
+ return;
+ }
+
+ DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));
+
+ //
+ // Parse RAM in E820 map
+ //
+ E820EntriesCount = 0;
+ Status = XenGetE820Map (&E820Map, &E820EntriesCount);
+
+ ASSERT_EFI_ERROR (Status);
+
+ if (E820EntriesCount > 0) {
+ EFI_E820_ENTRY64 *Entry;
+ UINT32 Loop;
+
+ for (Loop = 0; Loop < E820EntriesCount; Loop++) {
+ Entry = E820Map + Loop;
+
+ //
+ // Only care about RAM
+ //
+ if (Entry->Type != EfiAcpiAddressRangeMemory) {
+ continue;
+ }
+
+ AddMemoryBaseSizeHob (Entry->BaseAddr, Entry->Length);
+
+ MtrrSetMemoryAttribute (Entry->BaseAddr, Entry->Length, CacheWriteBack);
+ }
+ }
+}
+
+
+/**
+ Perform Xen PEI initialization.
+
+ @return EFI_SUCCESS Xen initialized successfully
+ @return EFI_NOT_FOUND Not running under Xen
+
+**/
+EFI_STATUS
+InitializeXen (
+ VOID
+ )
+{
+ RETURN_STATUS PcdStatus;
+
+ if (mXenLeaf == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ XenConnect (mXenLeaf);
+
+ //
+ // Reserve away HVMLOADER reserved memory [0xFC000000,0xFD000000).
+ // This needs to match HVMLOADER RESERVED_MEMBASE/RESERVED_MEMSIZE.
+ //
+ AddReservedMemoryBaseSizeHob (0xFC000000, 0x1000000, FALSE);
+
+ PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, TRUE);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/OvmfPkg/PlatformPei/Xen.h b/roms/edk2/OvmfPkg/PlatformPei/Xen.h new file mode 100644 index 000000000..260548128 --- /dev/null +++ b/roms/edk2/OvmfPkg/PlatformPei/Xen.h @@ -0,0 +1,39 @@ +/** @file
+ Ovmf info structure passed by Xen
+
+Copyright (c) 2013, Citrix Systems UK Ltd.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __XEN_H__
+#define __XEN_H__
+
+#include <PiPei.h>
+
+// Physical address of OVMF info
+#define OVMF_INFO_PHYSICAL_ADDRESS 0x00001000
+
+// This structure must match the definition on Xen side
+#pragma pack(1)
+typedef struct {
+ CHAR8 Signature[14]; // XenHVMOVMF\0
+ UINT8 Length; // Length of this structure
+ UINT8 Checksum; // Set such that the sum over bytes 0..length == 0
+ //
+ // Physical address of an array of TablesCount elements.
+ //
+ // Each element contains the physical address of a BIOS table.
+ //
+ EFI_PHYSICAL_ADDRESS Tables;
+ UINT32 TablesCount;
+ //
+ // Physical address of the E820 table, contains E820EntriesCount entries.
+ //
+ EFI_PHYSICAL_ADDRESS E820;
+ UINT32 E820EntriesCount;
+} EFI_XEN_OVMF_INFO;
+#pragma pack()
+
+#endif /* __XEN_H__ */
|