Add BhyvePkg, to support the bhyve hypervisor

BhyvePkg supports the bhyve hypervisor, which is a hypervisor/virtual
machine manager available on FreeBSD, macOS and Illumos.

Signed-off-by: Rebecca Cran <rebecca@bsdio.com>
Message-Id: <20200713054131.479627-2-rebecca@bsdio.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
Rebecca Cran
2020-07-12 23:41:31 -06:00
committed by mergify[bot]
parent 137c2c6eff
commit 656419f922
53 changed files with 12283 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
/**@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/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/PcdLib.h>
#include <PiPei.h>
#include <Register/Amd/Cpuid.h>
#include <Register/Cpuid.h>
#include <Register/Intel/SmramSaveStateMap.h>
#include "Platform.h"
/**
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
);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,21 @@
/**@file
Install a callback when necessary for setting the Feature Control MSR on all
processors.
Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
Copyright (C) 2016, Red Hat, Inc.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Platform.h"
VOID
InstallFeatureControlCallback (
VOID
)
{
//
// Nothing to do.
//
}

View File

@@ -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;
}

View File

@@ -0,0 +1,627 @@
/**@file
Memory Detection for Virtual Machines.
Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
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/Q35MchIch9.h>
#include <PiPei.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/PcdLib.h>
#include <Library/PciLib.h>
#include <Library/PeimEntryPoint.h>
#include <Library/ResourcePublicationLib.h>
#include <Library/MtrrLib.h>
#include "Platform.h"
#include "Cmos.h"
UINT8 mPhysMemAddressWidth;
STATIC UINT32 mS3AcpiReservedMemoryBase;
STATIC UINT32 mS3AcpiReservedMemorySize;
STATIC UINT16 mQ35TsegMbytes;
BOOLEAN mQ35SmramAtDefaultSmbase = FALSE;
VOID
Q35TsegMbytesInitialization (
VOID
)
{
UINT16 ExtendedTsegMbytes;
RETURN_STATUS PcdStatus;
if (mHostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {
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 ();
}
//
// 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;
}
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;
RETURN_STATUS PcdStatus;
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);
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;
}
//
// 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;
}
}
//
// Publish this memory to the PEI Core
//
Status = PublishSystemMemory(MemoryBase, MemorySize);
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
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.
//
AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
} else {
//
// Create memory HOBs
//
AddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
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 (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 memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as
// uncacheable
//
Status = MtrrSetMemoryAttribute (LowerMemorySize,
SIZE_4GB - LowerMemorySize, CacheUncacheable);
ASSERT_EFI_ERROR (Status);
}
}
/**
Publish system RAM and reserve memory regions
**/
VOID
InitializeRamRegions (
VOID
)
{
QemuInitializeRam ();
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
);
#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
);
}
}
}

View File

@@ -0,0 +1,607 @@
/**@file
Platform PEI driver
Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
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/ResourcePublicationLib.h>
#include <Library/LocalApicLib.h>
#include <Guid/MemoryTypeInformation.h>
#include <Ppi/MasterBootMode.h>
#include <IndustryStandard/Pci22.h>
#include <OvmfPlatforms.h>
#include "Platform.h"
#include "Cmos.h"
EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = {
{ EfiACPIMemoryNVS, 0x004 },
{ EfiACPIReclaimMemory, 0x008 },
{ EfiReservedMemoryType, 0x004 },
{ EfiRuntimeServicesData, 0x024 },
{ EfiRuntimeServicesCode, 0x030 },
{ EfiBootServicesCode, 0x180 },
{ EfiBootServicesData, 0xF00 },
{ EfiMaxMemoryType, 0x000 }
};
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;
//
// Create Memory Type Information HOB
//
BuildGuidDataHob (
&gEfiMemoryTypeInformationGuid,
mDefaultMemoryTypeInformation,
sizeof(mDefaultMemoryTypeInformation)
);
//
// Video memory + Legacy BIOS region
//
AddIoMemoryRangeHob (0x0A0000, BASE_1MB);
if (TRUE) {
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 {
PciBase = (TopOfLowRam < BASE_2GB) ? BASE_2GB : TopOfLowRam;
}
//
// 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);
}
VOID
NoexecDxeInitialization (
VOID
)
{
}
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 0x1275: // BHYVE
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
}
/**
Fetch the number of boot CPUs from QEMU and expose it to UefiCpuPkg modules.
Set the mMaxCpuCount variable.
**/
VOID
MaxCpuCountInitialization (
VOID
)
{
UINT16 ProcessorCount = 0;
RETURN_STATUS PcdStatus;
//
// If the fw_cfg key or fw_cfg entirely is unavailable, load mMaxCpuCount
// from the PCD default. No change to PCDs.
//
if (ProcessorCount == 0) {
mMaxCpuCount = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
return;
}
//
// Otherwise, set mMaxCpuCount to the value reported by QEMU.
//
mMaxCpuCount = ProcessorCount;
//
// Additionally, tell UefiCpuPkg modules (a) the exact number of VCPUs, (b)
// to wait, in the initial AP bringup, exactly as long as it takes for all of
// the APs to report in. For this, we set the longest representable timeout
// (approx. 71 minutes).
//
PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, ProcessorCount);
ASSERT_RETURN_ERROR (PcdStatus);
PcdStatus = PcdSet32S (PcdCpuApInitTimeOutInMicroSeconds, MAX_UINT32);
ASSERT_RETURN_ERROR (PcdStatus);
DEBUG ((DEBUG_INFO, "%a: QEMU reports %d processor(s)\n", __FUNCTION__,
ProcessorCount));
}
/**
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
)
{
DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n"));
//
// Initialize Local APIC Timer hardware and disable Local APIC Timer
// interrupts before initializing the Debug Agent and the debug timer is
// enabled.
//
InitializeApicTimer (0, MAX_UINT32, TRUE, 5);
DisableApicTimerInterrupt ();
DebugDumpCmos ();
BootModeInitialization ();
AddressWidthInitialization ();
MaxCpuCountInitialization ();
//
// Query Host Bridge DID
//
mHostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
if (FeaturePcdGet (PcdSmmSmramRequire)) {
Q35TsegMbytesInitialization ();
}
PublishPeiMemory ();
InitializeRamRegions ();
if (mBootMode != BOOT_ON_S3_RESUME) {
if (!FeaturePcdGet (PcdSmmSmramRequire)) {
ReserveEmuVariableNvStore ();
}
PeiFvInitialization ();
MemMapInitialization ();
NoexecDxeInitialization ();
}
InstallClearCacheCallback ();
AmdSevInitialize ();
MiscInitialization ();
InstallFeatureControlCallback ();
return EFI_SUCCESS;
}

View File

@@ -0,0 +1,137 @@
/** @file
Platform PEI module include file.
Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
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_

View File

@@ -0,0 +1,113 @@
## @file
# Platform PEI driver
#
# This module provides platform specific function to detect boot mode.
#
# Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = PlatformPei
FILE_GUID = aa89d903-345b-4ab2-9abf-030b5efb5d50
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
Platform.c
Platform.h
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
SecurityPkg/SecurityPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
OvmfPkg/OvmfPkg.dec
[Guids]
gEfiMemoryTypeInformationGuid
[LibraryClasses]
BaseLib
CacheMaintenanceLib
DebugLib
HobLib
IoLib
PciLib
ResourcePublicationLib
PeiServicesLib
PeiServicesTablePointerLib
PeimEntryPoint
MtrrLib
MemEncryptSevLib
PcdLib
LocalApicLib
[Pcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId
gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase
gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize
gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base
gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size
gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base
gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd
gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes
gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved
gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode
gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable
gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy
gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds
gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
[FixedPcd]
gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
[FeaturePcd]
gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
[Ppis]
gEfiPeiMasterBootModePpiGuid
gEfiPeiMpServicesPpiGuid
[Depex]
TRUE