blob: 20a1a9cdbc6ed64c546a92f30c4030be5625e2b6 [file] [log] [blame]
/** @file
Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU.
Copyright (c) 2009 - 2024, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PiSmmCpuDxeSmm.h"
//
// SMM CPU Private Data structure that contains SMM Configuration Protocol
// along its supporting fields.
//
SMM_CPU_PRIVATE_DATA mSmmCpuPrivateData = {
SMM_CPU_PRIVATE_DATA_SIGNATURE, // Signature
NULL, // SmmCpuHandle
NULL, // Pointer to ProcessorInfo array
NULL, // Pointer to Operation array
NULL, // Pointer to CpuSaveStateSize array
NULL, // Pointer to CpuSaveState array
{
{ 0 }
}, // SmmReservedSmramRegion
{
SmmStartupThisAp, // SmmCoreEntryContext.SmmStartupThisAp
0, // SmmCoreEntryContext.CurrentlyExecutingCpu
0, // SmmCoreEntryContext.NumberOfCpus
NULL, // SmmCoreEntryContext.CpuSaveStateSize
NULL // SmmCoreEntryContext.CpuSaveState
},
NULL, // SmmCoreEntry
{
mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions
RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry
},
NULL, // pointer to Ap Wrapper Func array
{ NULL, NULL }, // List_Entry for Tokens.
};
CPU_HOT_PLUG_DATA mCpuHotPlugData = {
CPU_HOT_PLUG_DATA_REVISION_1, // Revision
0, // Array Length of SmBase and APIC ID
NULL, // Pointer to APIC ID array
NULL, // Pointer to SMBASE array
0, // Reserved
0, // SmrrBase
0 // SmrrSize
};
//
// Global pointer used to access mSmmCpuPrivateData from outside and inside SMM
//
SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate = &mSmmCpuPrivateData;
///
/// Handle for the SMM CPU Protocol
///
EFI_HANDLE mSmmCpuHandle = NULL;
///
/// SMM CPU Protocol instance
///
EFI_SMM_CPU_PROTOCOL mSmmCpu = {
SmmReadSaveState,
SmmWriteSaveState
};
///
/// SMM Memory Attribute Protocol instance
///
EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL mSmmMemoryAttribute = {
EdkiiSmmGetMemoryAttributes,
EdkiiSmmSetMemoryAttributes,
EdkiiSmmClearMemoryAttributes
};
EFI_CPU_INTERRUPT_HANDLER mExternalVectorTable[EXCEPTION_VECTOR_NUMBER];
volatile BOOLEAN *mSmmInitialized = NULL;
UINT32 mBspApicId = 0;
//
// SMM stack information
//
UINTN mSmmStackArrayBase;
UINTN mSmmStackArrayEnd;
UINTN mSmmStackSize;
UINTN mSmmShadowStackSize;
BOOLEAN mCetSupported = TRUE;
UINTN mMaxNumberOfCpus = 0;
UINTN mNumberOfCpus = 0;
//
// SMM ready to lock flag
//
BOOLEAN mSmmReadyToLock = FALSE;
//
// Global used to cache PCD for SMM Code Access Check enable
//
BOOLEAN mSmmCodeAccessCheckEnable = FALSE;
//
// Global used to cache SMM Debug Agent Supported ot not
//
BOOLEAN mSmmDebugAgentSupport = FALSE;
//
// Global copy of the PcdPteMemoryEncryptionAddressOrMask
//
UINT64 mAddressEncMask = 0;
//
// Spin lock used to serialize setting of SMM Code Access Check feature
//
SPIN_LOCK *mConfigSmmCodeAccessCheckLock = NULL;
//
// Saved SMM ranges information
//
EFI_SMRAM_DESCRIPTOR *mSmmCpuSmramRanges;
UINTN mSmmCpuSmramRangeCount;
UINT8 mPhysicalAddressBits;
/**
Initialize IDT to setup exception handlers for SMM.
**/
VOID
InitializeSmmIdt (
VOID
)
{
EFI_STATUS Status;
BOOLEAN InterruptState;
IA32_DESCRIPTOR DxeIdtr;
//
// There are 32 (not 255) entries in it since only processor
// generated exceptions will be handled.
//
gcSmiIdtr.Limit = (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32) - 1;
//
// Allocate page aligned IDT, because it might be set as read only.
//
gcSmiIdtr.Base = (UINTN)AllocateCodePages (EFI_SIZE_TO_PAGES (gcSmiIdtr.Limit + 1));
ASSERT (gcSmiIdtr.Base != 0);
ZeroMem ((VOID *)gcSmiIdtr.Base, gcSmiIdtr.Limit + 1);
//
// Disable Interrupt and save DXE IDT table
//
InterruptState = SaveAndDisableInterrupts ();
AsmReadIdtr (&DxeIdtr);
//
// Load SMM temporary IDT table
//
AsmWriteIdtr (&gcSmiIdtr);
//
// Setup SMM default exception handlers, SMM IDT table
// will be updated and saved in gcSmiIdtr
//
Status = InitializeCpuExceptionHandlers (NULL);
ASSERT_EFI_ERROR (Status);
//
// Restore DXE IDT table and CPU interrupt
//
AsmWriteIdtr ((IA32_DESCRIPTOR *)&DxeIdtr);
SetInterruptState (InterruptState);
}
/**
Search module name by input IP address and output it.
@param CallerIpAddress Caller instruction pointer.
**/
VOID
DumpModuleInfoByIp (
IN UINTN CallerIpAddress
)
{
UINTN Pe32Data;
VOID *PdbPointer;
//
// Find Image Base
//
Pe32Data = PeCoffSearchImageBase (CallerIpAddress);
if (Pe32Data != 0) {
DEBUG ((DEBUG_ERROR, "It is invoked from the instruction before IP(0x%p)", (VOID *)CallerIpAddress));
PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)Pe32Data);
if (PdbPointer != NULL) {
DEBUG ((DEBUG_ERROR, " in module (%a)\n", PdbPointer));
}
}
}
/**
Read information from the CPU save state.
@param This EFI_SMM_CPU_PROTOCOL instance
@param Width The number of bytes to read from the CPU save state.
@param Register Specifies the CPU register to read form the save state.
@param CpuIndex Specifies the zero-based index of the CPU save state.
@param Buffer Upon return, this holds the CPU register value read from the save state.
@retval EFI_SUCCESS The register was read from Save State
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
@retval EFI_INVALID_PARAMETER This or Buffer is NULL.
**/
EFI_STATUS
EFIAPI
SmmReadSaveState (
IN CONST EFI_SMM_CPU_PROTOCOL *This,
IN UINTN Width,
IN EFI_SMM_SAVE_STATE_REGISTER Register,
IN UINTN CpuIndex,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
//
// Retrieve pointer to the specified CPU's SMM Save State buffer
//
if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// The SpeculationBarrier() call here is to ensure the above check for the
// CpuIndex has been completed before the execution of subsequent codes.
//
SpeculationBarrier ();
//
// Check for special EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID
//
if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
//
// The pseudo-register only supports the 64-bit size specified by Width.
//
if (Width != sizeof (UINT64)) {
return EFI_INVALID_PARAMETER;
}
//
// If the processor is in SMM at the time the SMI occurred,
// the pseudo register value for EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID is returned in Buffer.
// Otherwise, EFI_NOT_FOUND is returned.
//
if (*(mSmmMpSyncData->CpuData[CpuIndex].Present)) {
*(UINT64 *)Buffer = gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId;
return EFI_SUCCESS;
} else {
return EFI_NOT_FOUND;
}
}
if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) {
return EFI_INVALID_PARAMETER;
}
Status = MmSaveStateReadRegister (CpuIndex, Register, Width, Buffer);
return Status;
}
/**
Write data to the CPU save state.
@param This EFI_SMM_CPU_PROTOCOL instance
@param Width The number of bytes to read from the CPU save state.
@param Register Specifies the CPU register to write to the save state.
@param CpuIndex Specifies the zero-based index of the CPU save state
@param Buffer Upon entry, this holds the new CPU register value.
@retval EFI_SUCCESS The register was written from Save State
@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor
@retval EFI_INVALID_PARAMETER ProcessorIndex or Width is not correct
**/
EFI_STATUS
EFIAPI
SmmWriteSaveState (
IN CONST EFI_SMM_CPU_PROTOCOL *This,
IN UINTN Width,
IN EFI_SMM_SAVE_STATE_REGISTER Register,
IN UINTN CpuIndex,
IN CONST VOID *Buffer
)
{
EFI_STATUS Status;
//
// Retrieve pointer to the specified CPU's SMM Save State buffer
//
if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Writes to EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID are ignored
//
if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) {
return EFI_SUCCESS;
}
if (!mSmmMpSyncData->CpuData[CpuIndex].Present) {
return EFI_INVALID_PARAMETER;
}
Status = MmSaveStateWriteRegister (CpuIndex, Register, Width, Buffer);
return Status;
}
/**
Initialize SMM environment.
**/
VOID
InitializeSmm (
VOID
)
{
UINT32 ApicId;
UINTN Index;
BOOLEAN IsBsp;
ApicId = GetApicId ();
IsBsp = (BOOLEAN)(mBspApicId == ApicId);
ASSERT (mNumberOfCpus <= mMaxNumberOfCpus);
for (Index = 0; Index < mNumberOfCpus; Index++) {
if (ApicId == (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) {
PERF_CODE (
MpPerfBegin (Index, SMM_MP_PERF_PROCEDURE_ID (InitializeSmm));
);
//
// Initialize SMM specific features on the currently executing CPU
//
SmmCpuFeaturesInitializeProcessor (
Index,
IsBsp,
gSmmCpuPrivate->ProcessorInfo,
&mCpuHotPlugData
);
if (!mSmmS3Flag) {
//
// Check XD and BTS features on each processor on normal boot
//
CheckFeatureSupported ();
} else if (IsBsp) {
//
// BSP rebase is already done above.
// Initialize private data during S3 resume
//
InitializeMpSyncData ();
}
PERF_CODE (
MpPerfEnd (Index, SMM_MP_PERF_PROCEDURE_ID (InitializeSmm));
);
return;
}
}
ASSERT (FALSE);
}
/**
Issue SMI IPI (All Excluding Self SMM IPI + BSP SMM IPI) to execute first SMI init.
**/
VOID
ExecuteFirstSmiInit (
VOID
)
{
UINTN Index;
PERF_FUNCTION_BEGIN ();
if (mSmmInitialized == NULL) {
mSmmInitialized = (BOOLEAN *)AllocatePool (sizeof (BOOLEAN) * mMaxNumberOfCpus);
}
ASSERT (mSmmInitialized != NULL);
if (mSmmInitialized == NULL) {
PERF_FUNCTION_END ();
return;
}
//
// Reset the mSmmInitialized to false.
//
ZeroMem ((VOID *)mSmmInitialized, sizeof (BOOLEAN) * mMaxNumberOfCpus);
//
// Get the BSP ApicId.
//
mBspApicId = GetApicId ();
//
// Issue SMI IPI (All Excluding Self SMM IPI + BSP SMM IPI) for SMM init
//
SendSmiIpi (mBspApicId);
SendSmiIpiAllExcludingSelf ();
//
// Wait for all processors to finish its 1st SMI
//
for (Index = 0; Index < mNumberOfCpus; Index++) {
while (!(BOOLEAN)mSmmInitialized[Index]) {
}
}
PERF_FUNCTION_END ();
}
/**
SMM Ready To Lock event notification handler.
mSmmReadyToLock is set to perform additional lock actions that must be
performed from SMM on the next SMI.
@param[in] Protocol Points to the protocol's unique identifier.
@param[in] Interface Points to the interface instance.
@param[in] Handle The handle on which the interface was installed.
@retval EFI_SUCCESS Notification handler runs successfully.
**/
EFI_STATUS
EFIAPI
SmmReadyToLockEventNotify (
IN CONST EFI_GUID *Protocol,
IN VOID *Interface,
IN EFI_HANDLE Handle
)
{
//
// Cache a copy of UEFI memory map before we start profiling feature.
//
GetUefiMemoryMap ();
//
// Set SMM ready to lock flag and return
//
mSmmReadyToLock = TRUE;
return EFI_SUCCESS;
}
/**
Function to compare 2 SMM_BASE_HOB_DATA pointer based on ProcessorIndex.
@param[in] Buffer1 pointer to SMM_BASE_HOB_DATA poiner to compare
@param[in] Buffer2 pointer to second SMM_BASE_HOB_DATA pointer to compare
@retval 0 Buffer1 equal to Buffer2
@retval <0 Buffer1 is less than Buffer2
@retval >0 Buffer1 is greater than Buffer2
**/
INTN
EFIAPI
SmBaseHobCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
)
{
if ((*(SMM_BASE_HOB_DATA **)Buffer1)->ProcessorIndex > (*(SMM_BASE_HOB_DATA **)Buffer2)->ProcessorIndex) {
return 1;
} else if ((*(SMM_BASE_HOB_DATA **)Buffer1)->ProcessorIndex < (*(SMM_BASE_HOB_DATA **)Buffer2)->ProcessorIndex) {
return -1;
}
return 0;
}
/**
Extract SmBase for all CPU from SmmBase HOB.
@param[in] MaxNumberOfCpus Max NumberOfCpus.
@param[out] AllocatedSmBaseBuffer Pointer to SmBase Buffer allocated
by this function. Only set if the
function returns EFI_SUCCESS.
@retval EFI_SUCCESS SmBase Buffer output successfully.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_NOT_FOUND gSmmBaseHobGuid was never created.
**/
STATIC
EFI_STATUS
GetSmBase (
IN UINTN MaxNumberOfCpus,
OUT UINTN **AllocatedSmBaseBuffer
)
{
UINTN HobCount;
EFI_HOB_GUID_TYPE *GuidHob;
SMM_BASE_HOB_DATA *SmmBaseHobData;
UINTN NumberOfProcessors;
SMM_BASE_HOB_DATA **SmBaseHobs;
UINTN *SmBaseBuffer;
UINTN HobIndex;
UINTN SortBuffer;
UINTN ProcessorIndex;
UINT64 PrevProcessorIndex;
EFI_HOB_GUID_TYPE *FirstSmmBaseGuidHob;
SmmBaseHobData = NULL;
HobIndex = 0;
ProcessorIndex = 0;
HobCount = 0;
NumberOfProcessors = 0;
FirstSmmBaseGuidHob = GetFirstGuidHob (&gSmmBaseHobGuid);
if (FirstSmmBaseGuidHob == NULL) {
return EFI_NOT_FOUND;
}
GuidHob = FirstSmmBaseGuidHob;
while (GuidHob != NULL) {
HobCount++;
SmmBaseHobData = GET_GUID_HOB_DATA (GuidHob);
NumberOfProcessors += SmmBaseHobData->NumberOfProcessors;
if (NumberOfProcessors >= MaxNumberOfCpus) {
break;
}
GuidHob = GetNextGuidHob (&gSmmBaseHobGuid, GET_NEXT_HOB (GuidHob));
}
ASSERT (NumberOfProcessors == MaxNumberOfCpus);
if (NumberOfProcessors != MaxNumberOfCpus) {
CpuDeadLoop ();
}
SmBaseHobs = AllocatePool (sizeof (SMM_BASE_HOB_DATA *) * HobCount);
if (SmBaseHobs == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Record each SmmBaseHob pointer in the SmBaseHobs.
// The FirstSmmBaseGuidHob is to speed up this while-loop
// without needing to look for SmBaseHob from beginning.
//
GuidHob = FirstSmmBaseGuidHob;
while (HobIndex < HobCount) {
SmBaseHobs[HobIndex++] = GET_GUID_HOB_DATA (GuidHob);
GuidHob = GetNextGuidHob (&gSmmBaseHobGuid, GET_NEXT_HOB (GuidHob));
}
SmBaseBuffer = (UINTN *)AllocatePool (sizeof (UINTN) * (MaxNumberOfCpus));
ASSERT (SmBaseBuffer != NULL);
if (SmBaseBuffer == NULL) {
FreePool (SmBaseHobs);
return EFI_OUT_OF_RESOURCES;
}
QuickSort (SmBaseHobs, HobCount, sizeof (SMM_BASE_HOB_DATA *), (BASE_SORT_COMPARE)SmBaseHobCompare, &SortBuffer);
PrevProcessorIndex = 0;
for (HobIndex = 0; HobIndex < HobCount; HobIndex++) {
//
// Make sure no overlap and no gap in the CPU range covered by each HOB
//
ASSERT (SmBaseHobs[HobIndex]->ProcessorIndex == PrevProcessorIndex);
//
// Cache each SmBase in order.
//
for (ProcessorIndex = 0; ProcessorIndex < SmBaseHobs[HobIndex]->NumberOfProcessors; ProcessorIndex++) {
SmBaseBuffer[PrevProcessorIndex + ProcessorIndex] = (UINTN)SmBaseHobs[HobIndex]->SmBase[ProcessorIndex];
}
PrevProcessorIndex += SmBaseHobs[HobIndex]->NumberOfProcessors;
}
FreePool (SmBaseHobs);
*AllocatedSmBaseBuffer = SmBaseBuffer;
return EFI_SUCCESS;
}
/**
Function to compare 2 MP_INFORMATION2_HOB_DATA pointer based on ProcessorIndex.
@param[in] Buffer1 pointer to MP_INFORMATION2_HOB_DATA poiner to compare
@param[in] Buffer2 pointer to second MP_INFORMATION2_HOB_DATA pointer to compare
@retval 0 Buffer1 equal to Buffer2
@retval <0 Buffer1 is less than Buffer2
@retval >0 Buffer1 is greater than Buffer2
**/
INTN
EFIAPI
MpInformation2HobCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
)
{
if ((*(MP_INFORMATION2_HOB_DATA **)Buffer1)->ProcessorIndex > (*(MP_INFORMATION2_HOB_DATA **)Buffer2)->ProcessorIndex) {
return 1;
} else if ((*(MP_INFORMATION2_HOB_DATA **)Buffer1)->ProcessorIndex < (*(MP_INFORMATION2_HOB_DATA **)Buffer2)->ProcessorIndex) {
return -1;
}
return 0;
}
/**
Extract NumberOfCpus, MaxNumberOfCpus and EFI_PROCESSOR_INFORMATION for all CPU from gEfiMpServiceProtocolGuid.
@param[out] NumberOfCpus Pointer to NumberOfCpus.
@param[out] MaxNumberOfCpus Pointer to MaxNumberOfCpus.
@retval ProcessorInfo Pointer to EFI_PROCESSOR_INFORMATION buffer.
**/
EFI_PROCESSOR_INFORMATION *
GetMpInformationFromMpServices (
OUT UINTN *NumberOfCpus,
OUT UINTN *MaxNumberOfCpus
)
{
EFI_STATUS Status;
UINTN Index;
UINTN NumberOfEnabledProcessors;
UINTN NumberOfProcessors;
EFI_MP_SERVICES_PROTOCOL *MpService;
EFI_PROCESSOR_INFORMATION *ProcessorInfo;
if ((NumberOfCpus == NULL) || (MaxNumberOfCpus == NULL)) {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
return NULL;
}
ProcessorInfo = NULL;
*NumberOfCpus = 0;
*MaxNumberOfCpus = 0;
/// Get the MP Services Protocol
Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return NULL;
}
/// Get the number of processors
Status = MpService->GetNumberOfProcessors (MpService, &NumberOfProcessors, &NumberOfEnabledProcessors);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return NULL;
}
ASSERT (NumberOfProcessors <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
/// Allocate buffer for processor information
ProcessorInfo = AllocateZeroPool (sizeof (EFI_PROCESSOR_INFORMATION) * NumberOfProcessors);
if (ProcessorInfo == NULL) {
ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
return NULL;
}
/// Get processor information
for (Index = 0; Index < NumberOfProcessors; Index++) {
Status = MpService->GetProcessorInfo (MpService, Index | CPU_V2_EXTENDED_TOPOLOGY, &ProcessorInfo[Index]);
if (EFI_ERROR (Status)) {
FreePool (ProcessorInfo);
DEBUG ((DEBUG_ERROR, "%a: Failed to get processor information for processor %d\n", __func__, Index));
ASSERT_EFI_ERROR (Status);
return NULL;
}
}
*NumberOfCpus = NumberOfEnabledProcessors;
ASSERT (*NumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
//
// If support CPU hot plug, we need to allocate resources for possibly hot-added processors
//
if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
*MaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
} else {
*MaxNumberOfCpus = *NumberOfCpus;
}
return ProcessorInfo;
}
/**
Extract NumberOfCpus, MaxNumberOfCpus and EFI_PROCESSOR_INFORMATION for all CPU from MpInformation2 HOB.
@param[out] NumberOfCpus Pointer to NumberOfCpus.
@param[out] MaxNumberOfCpus Pointer to MaxNumberOfCpus.
@retval ProcessorInfo Pointer to EFI_PROCESSOR_INFORMATION buffer.
**/
EFI_PROCESSOR_INFORMATION *
GetMpInformation (
OUT UINTN *NumberOfCpus,
OUT UINTN *MaxNumberOfCpus
)
{
EFI_HOB_GUID_TYPE *GuidHob;
EFI_HOB_GUID_TYPE *FirstMpInfo2Hob;
MP_INFORMATION2_HOB_DATA *MpInformation2HobData;
UINTN HobCount;
UINTN HobIndex;
MP_INFORMATION2_HOB_DATA **MpInfo2Hobs;
UINTN SortBuffer;
UINTN ProcessorIndex;
UINT64 PrevProcessorIndex;
MP_INFORMATION2_ENTRY *MpInformation2Entry;
EFI_PROCESSOR_INFORMATION *ProcessorInfo;
GuidHob = NULL;
MpInformation2HobData = NULL;
FirstMpInfo2Hob = NULL;
MpInfo2Hobs = NULL;
HobIndex = 0;
HobCount = 0;
FirstMpInfo2Hob = GetFirstGuidHob (&gMpInformation2HobGuid);
if (FirstMpInfo2Hob == NULL) {
DEBUG ((DEBUG_INFO, "%a: [INFO] gMpInformation2HobGuid HOB not found.\n", __func__));
return GetMpInformationFromMpServices (NumberOfCpus, MaxNumberOfCpus);
}
GuidHob = FirstMpInfo2Hob;
while (GuidHob != NULL) {
MpInformation2HobData = GET_GUID_HOB_DATA (GuidHob);
//
// This is the last MpInformationHob in the HOB list.
//
if (MpInformation2HobData->NumberOfProcessors == 0) {
ASSERT (HobCount != 0);
break;
}
HobCount++;
*NumberOfCpus += MpInformation2HobData->NumberOfProcessors;
GuidHob = GetNextGuidHob (&gMpInformation2HobGuid, GET_NEXT_HOB (GuidHob));
}
ASSERT (*NumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
//
// If support CPU hot plug, we need to allocate resources for possibly hot-added processors
//
if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
*MaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
} else {
*MaxNumberOfCpus = *NumberOfCpus;
}
MpInfo2Hobs = AllocatePool (sizeof (MP_INFORMATION2_HOB_DATA *) * HobCount);
ASSERT (MpInfo2Hobs != NULL);
if (MpInfo2Hobs == NULL) {
return NULL;
}
//
// Record each MpInformation2Hob pointer in the MpInfo2Hobs.
// The FirstMpInfo2Hob is to speed up this while-loop without
// needing to look for MpInfo2Hob from beginning.
//
GuidHob = FirstMpInfo2Hob;
while (HobIndex < HobCount) {
MpInfo2Hobs[HobIndex++] = GET_GUID_HOB_DATA (GuidHob);
GuidHob = GetNextGuidHob (&gMpInformation2HobGuid, GET_NEXT_HOB (GuidHob));
}
ProcessorInfo = (EFI_PROCESSOR_INFORMATION *)AllocatePool (sizeof (EFI_PROCESSOR_INFORMATION) * (*MaxNumberOfCpus));
ASSERT (ProcessorInfo != NULL);
if (ProcessorInfo == NULL) {
FreePool (MpInfo2Hobs);
return NULL;
}
QuickSort (MpInfo2Hobs, HobCount, sizeof (MP_INFORMATION2_HOB_DATA *), (BASE_SORT_COMPARE)MpInformation2HobCompare, &SortBuffer);
PrevProcessorIndex = 0;
for (HobIndex = 0; HobIndex < HobCount; HobIndex++) {
//
// Make sure no overlap and no gap in the CPU range covered by each HOB
//
ASSERT (MpInfo2Hobs[HobIndex]->ProcessorIndex == PrevProcessorIndex);
//
// Cache each EFI_PROCESSOR_INFORMATION in order.
//
for (ProcessorIndex = 0; ProcessorIndex < MpInfo2Hobs[HobIndex]->NumberOfProcessors; ProcessorIndex++) {
MpInformation2Entry = GET_MP_INFORMATION_ENTRY (MpInfo2Hobs[HobIndex], ProcessorIndex);
CopyMem (
&ProcessorInfo[PrevProcessorIndex + ProcessorIndex],
&MpInformation2Entry->ProcessorInfo,
sizeof (EFI_PROCESSOR_INFORMATION)
);
}
PrevProcessorIndex += MpInfo2Hobs[HobIndex]->NumberOfProcessors;
}
FreePool (MpInfo2Hobs);
return ProcessorInfo;
}
/**
The module Entry Point of the CPU SMM driver.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval Other Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
PiCpuSmmEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN Index;
UINTN TileCodeSize;
UINTN TileDataSize;
UINTN TileSize;
UINT8 *Stacks;
VOID *Registration;
UINT32 RegEax;
UINT32 RegEbx;
UINT32 RegEcx;
UINT32 RegEdx;
UINTN FamilyId;
UINTN ModelId;
UINT32 Cr3;
PERF_FUNCTION_BEGIN ();
//
// Initialize address fixup
//
PiSmmCpuSmiEntryFixupAddress ();
//
// Initialize Debug Agent to support source level debug in SMM code
//
InitializeDebugAgent (DEBUG_AGENT_INIT_SMM, &mSmmDebugAgentSupport, NULL);
//
// Report the start of CPU SMM initialization.
//
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_PC_SMM_INIT
);
//
// Find out SMRR Base and SMRR Size
//
FindSmramInfo (&mCpuHotPlugData.SmrrBase, &mCpuHotPlugData.SmrrSize);
//
// Retrive NumberOfProcessors, MaxNumberOfCpus and EFI_PROCESSOR_INFORMATION for all CPU from MpInformation2 HOB.
//
gSmmCpuPrivate->ProcessorInfo = GetMpInformation (&mNumberOfCpus, &mMaxNumberOfCpus);
ASSERT (gSmmCpuPrivate->ProcessorInfo != NULL);
//
// If support CPU hot plug, PcdCpuSmmEnableBspElection should be set to TRUE.
// A constant BSP index makes no sense because it may be hot removed.
//
DEBUG_CODE_BEGIN ();
if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
ASSERT (FeaturePcdGet (PcdCpuSmmEnableBspElection));
}
DEBUG_CODE_END ();
//
// Save the PcdCpuSmmCodeAccessCheckEnable value into a global variable.
//
mSmmCodeAccessCheckEnable = PcdGetBool (PcdCpuSmmCodeAccessCheckEnable);
DEBUG ((DEBUG_INFO, "PcdCpuSmmCodeAccessCheckEnable = %d\n", mSmmCodeAccessCheckEnable));
//
// Save the PcdPteMemoryEncryptionAddressOrMask value into a global variable.
// Make sure AddressEncMask is contained to smallest supported address field.
//
mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
DEBUG ((DEBUG_INFO, "mAddressEncMask = 0x%lx\n", mAddressEncMask));
gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus = mMaxNumberOfCpus;
PERF_CODE (
InitializeMpPerf (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);
);
//
// The CPU save state and code for the SMI entry point are tiled within an SMRAM
// allocated buffer. The minimum size of this buffer for a uniprocessor system
// is 32 KB, because the entry point is SMBASE + 32KB, and CPU save state area
// just below SMBASE + 64KB. If more than one CPU is present in the platform,
// then the SMI entry point and the CPU save state areas can be tiles to minimize
// the total amount SMRAM required for all the CPUs. The tile size can be computed
// by adding the // CPU save state size, any extra CPU specific context, and
// the size of code that must be placed at the SMI entry point to transfer
// control to a C function in the native SMM execution mode. This size is
// rounded up to the nearest power of 2 to give the tile size for a each CPU.
// The total amount of memory required is the maximum number of CPUs that
// platform supports times the tile size. The picture below shows the tiling,
// where m is the number of tiles that fit in 32KB.
//
// +-----------------------------+ <-- 2^n offset from Base of allocated buffer
// | CPU m+1 Save State |
// +-----------------------------+
// | CPU m+1 Extra Data |
// +-----------------------------+
// | Padding |
// +-----------------------------+
// | CPU 2m SMI Entry |
// +#############################+ <-- Base of allocated buffer + 64 KB
// | CPU m-1 Save State |
// +-----------------------------+
// | CPU m-1 Extra Data |
// +-----------------------------+
// | Padding |
// +-----------------------------+
// | CPU 2m-1 SMI Entry |
// +=============================+ <-- 2^n offset from Base of allocated buffer
// | . . . . . . . . . . . . |
// +=============================+ <-- 2^n offset from Base of allocated buffer
// | CPU 2 Save State |
// +-----------------------------+
// | CPU 2 Extra Data |
// +-----------------------------+
// | Padding |
// +-----------------------------+
// | CPU m+1 SMI Entry |
// +=============================+ <-- Base of allocated buffer + 32 KB
// | CPU 1 Save State |
// +-----------------------------+
// | CPU 1 Extra Data |
// +-----------------------------+
// | Padding |
// +-----------------------------+
// | CPU m SMI Entry |
// +#############################+ <-- Base of allocated buffer + 32 KB == CPU 0 SMBASE + 64 KB
// | CPU 0 Save State |
// +-----------------------------+
// | CPU 0 Extra Data |
// +-----------------------------+
// | Padding |
// +-----------------------------+
// | CPU m-1 SMI Entry |
// +=============================+ <-- 2^n offset from Base of allocated buffer
// | . . . . . . . . . . . . |
// +=============================+ <-- 2^n offset from Base of allocated buffer
// | Padding |
// +-----------------------------+
// | CPU 1 SMI Entry |
// +=============================+ <-- 2^n offset from Base of allocated buffer
// | Padding |
// +-----------------------------+
// | CPU 0 SMI Entry |
// +#############################+ <-- Base of allocated buffer == CPU 0 SMBASE + 32 KB
//
//
// Retrieve CPU Family
//
AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
FamilyId = (RegEax >> 8) & 0xf;
ModelId = (RegEax >> 4) & 0xf;
if ((FamilyId == 0x06) || (FamilyId == 0x0f)) {
ModelId = ModelId | ((RegEax >> 12) & 0xf0);
}
RegEdx = 0;
AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
}
//
// Determine the mode of the CPU at the time an SMI occurs
// Intel(R) 64 and IA-32 Architectures Software Developer's Manual
// Volume 3C, Section 34.4.1.1
//
mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT;
if ((RegEdx & BIT29) != 0) {
mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
}
if (FamilyId == 0x06) {
if ((ModelId == 0x17) || (ModelId == 0x0f) || (ModelId == 0x1c)) {
mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT;
}
}
DEBUG ((DEBUG_INFO, "PcdControlFlowEnforcementPropertyMask = %d\n", PcdGet32 (PcdControlFlowEnforcementPropertyMask)));
if (PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) {
AsmCpuid (CPUID_SIGNATURE, &RegEax, NULL, NULL, NULL);
if (RegEax >= CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {
AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, NULL, NULL, &RegEcx, &RegEdx);
DEBUG ((DEBUG_INFO, "CPUID[7/0] ECX - 0x%08x\n", RegEcx));
DEBUG ((DEBUG_INFO, " CET_SS - 0x%08x\n", RegEcx & CPUID_CET_SS));
DEBUG ((DEBUG_INFO, " CET_IBT - 0x%08x\n", RegEdx & CPUID_CET_IBT));
if ((RegEcx & CPUID_CET_SS) == 0) {
mCetSupported = FALSE;
PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1);
}
if (mCetSupported) {
AsmCpuidEx (CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF, NULL, &RegEbx, &RegEcx, NULL);
DEBUG ((DEBUG_INFO, "CPUID[D/1] EBX - 0x%08x, ECX - 0x%08x\n", RegEbx, RegEcx));
AsmCpuidEx (CPUID_EXTENDED_STATE, 11, &RegEax, NULL, &RegEcx, NULL);
DEBUG ((DEBUG_INFO, "CPUID[D/11] EAX - 0x%08x, ECX - 0x%08x\n", RegEax, RegEcx));
AsmCpuidEx (CPUID_EXTENDED_STATE, 12, &RegEax, NULL, &RegEcx, NULL);
DEBUG ((DEBUG_INFO, "CPUID[D/12] EAX - 0x%08x, ECX - 0x%08x\n", RegEax, RegEcx));
}
} else {
mCetSupported = FALSE;
PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1);
}
} else {
mCetSupported = FALSE;
PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1);
}
//
// Compute tile size of buffer required to hold the CPU SMRAM Save State Map, extra CPU
// specific context start starts at SMBASE + SMM_PSD_OFFSET, and the SMI entry point.
// This size is rounded up to nearest power of 2.
//
TileCodeSize = GetSmiHandlerSize ();
TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);
TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);
TileSize = TileDataSize + TileCodeSize - 1;
TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
DEBUG ((DEBUG_INFO, "SMRAM TileSize = 0x%08x (0x%08x, 0x%08x)\n", TileSize, TileCodeSize, TileDataSize));
//
// If the TileSize is larger than space available for the SMI Handler of
// CPU[i], the extra CPU specific context of CPU[i+1], and the SMRAM Save
// State Map of CPU[i+1], then ASSERT(). If this ASSERT() is triggered, then
// the SMI Handler size must be reduced or the size of the extra CPU specific
// context must be reduced.
//
ASSERT (TileSize <= (SMRAM_SAVE_STATE_MAP_OFFSET + sizeof (SMRAM_SAVE_STATE_MAP) - SMM_HANDLER_OFFSET));
//
// Check whether the Required TileSize is enough.
//
if (TileSize > SIZE_8KB) {
DEBUG ((DEBUG_ERROR, "The Range of Smbase in SMRAM is not enough -- Required TileSize = 0x%08x, Actual TileSize = 0x%08x\n", TileSize, SIZE_8KB));
FreePool (gSmmCpuPrivate->ProcessorInfo);
CpuDeadLoop ();
return RETURN_BUFFER_TOO_SMALL;
}
//
// Retrieve the allocated SmmBase from gSmmBaseHobGuid. If found,
// means the SmBase relocation has been done.
//
mCpuHotPlugData.SmBase = NULL;
Status = GetSmBase (mMaxNumberOfCpus, &mCpuHotPlugData.SmBase);
ASSERT (!EFI_ERROR (Status));
if (EFI_ERROR (Status)) {
CpuDeadLoop ();
}
//
// ASSERT SmBase has been relocated.
//
ASSERT (mCpuHotPlugData.SmBase != NULL);
//
// Allocate buffer for pointers to array in SMM_CPU_PRIVATE_DATA.
//
gSmmCpuPrivate->Operation = (SMM_CPU_OPERATION *)AllocatePool (sizeof (SMM_CPU_OPERATION) * mMaxNumberOfCpus);
ASSERT (gSmmCpuPrivate->Operation != NULL);
gSmmCpuPrivate->CpuSaveStateSize = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus);
ASSERT (gSmmCpuPrivate->CpuSaveStateSize != NULL);
gSmmCpuPrivate->CpuSaveState = (VOID **)AllocatePool (sizeof (VOID *) * mMaxNumberOfCpus);
ASSERT (gSmmCpuPrivate->CpuSaveState != NULL);
mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveStateSize = gSmmCpuPrivate->CpuSaveStateSize;
mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveState = gSmmCpuPrivate->CpuSaveState;
//
// Allocate buffer for pointers to array in CPU_HOT_PLUG_DATA.
//
mCpuHotPlugData.ApicId = (UINT64 *)AllocatePool (sizeof (UINT64) * mMaxNumberOfCpus);
ASSERT (mCpuHotPlugData.ApicId != NULL);
mCpuHotPlugData.ArrayLength = (UINT32)mMaxNumberOfCpus;
//
// Retrieve APIC ID of each enabled processor from the MP Services protocol.
// Also compute the SMBASE address, CPU Save State address, and CPU Save state
// size for each CPU in the platform
//
for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
gSmmCpuPrivate->CpuSaveStateSize[Index] = sizeof (SMRAM_SAVE_STATE_MAP);
gSmmCpuPrivate->CpuSaveState[Index] = (VOID *)(mCpuHotPlugData.SmBase[Index] + SMRAM_SAVE_STATE_MAP_OFFSET);
gSmmCpuPrivate->Operation[Index] = SmmCpuNone;
if (Index < mNumberOfCpus) {
mCpuHotPlugData.ApicId[Index] = gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId;
DEBUG ((
DEBUG_INFO,
"CPU[%03x] APIC ID=%04x SMBASE=%08x SaveState=%08x Size=%08x\n",
Index,
(UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId,
mCpuHotPlugData.SmBase[Index],
gSmmCpuPrivate->CpuSaveState[Index],
gSmmCpuPrivate->CpuSaveStateSize[Index]
));
} else {
gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = INVALID_APIC_ID;
mCpuHotPlugData.ApicId[Index] = INVALID_APIC_ID;
}
}
//
// Allocate SMI stacks for all processors.
//
mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
//
// SMM Stack Guard Enabled
// 2 more pages is allocated for each processor, one is guard page and the other is known good stack.
//
// +--------------------------------------------------+-----+--------------------------------------------------+
// | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |
// +--------------------------------------------------+-----+--------------------------------------------------+
// | 4K | 4K PcdCpuSmmStackSize| | 4K | 4K PcdCpuSmmStackSize|
// |<---------------- mSmmStackSize ----------------->| |<---------------- mSmmStackSize ----------------->|
// | | | |
// |<------------------ Processor 0 ----------------->| |<------------------ Processor n ----------------->|
//
mSmmStackSize += EFI_PAGES_TO_SIZE (2);
}
mSmmShadowStackSize = 0;
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
mSmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
//
// SMM Stack Guard Enabled
// Append Shadow Stack after normal stack
// 2 more pages is allocated for each processor, one is guard page and the other is known good shadow stack.
//
// |= Stacks
// +--------------------------------------------------+---------------------------------------------------------------+
// | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack |
// +--------------------------------------------------+---------------------------------------------------------------+
// | 4K | 4K |PcdCpuSmmStackSize| 4K | 4K |PcdCpuSmmShadowStackSize|
// |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->|
// | |
// |<-------------------------------------------- Processor N ------------------------------------------------------->|
//
mSmmShadowStackSize += EFI_PAGES_TO_SIZE (2);
} else {
//
// SMM Stack Guard Disabled (Known Good Stack is still required for potential stack switch.)
// Append Shadow Stack after normal stack with 1 more page as known good shadow stack.
// 1 more pages is allocated for each processor, it is known good stack.
//
//
// |= Stacks
// +-------------------------------------+--------------------------------------------------+
// | Known Good Stack | SMM Stack | Known Good Shadow Stack | SMM Shadow Stack |
// +-------------------------------------+--------------------------------------------------+
// | 4K |PcdCpuSmmStackSize| 4K |PcdCpuSmmShadowStackSize|
// |<---------- mSmmStackSize ---------->|<--------------- mSmmShadowStackSize ------------>|
// | |
// |<-------------------------------- Processor N ----------------------------------------->|
//
mSmmShadowStackSize += EFI_PAGES_TO_SIZE (1);
mSmmStackSize += EFI_PAGES_TO_SIZE (1);
}
}
Stacks = (UINT8 *)AllocatePages (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (EFI_SIZE_TO_PAGES (mSmmStackSize + mSmmShadowStackSize)));
ASSERT (Stacks != NULL);
mSmmStackArrayBase = (UINTN)Stacks;
mSmmStackArrayEnd = mSmmStackArrayBase + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (mSmmStackSize + mSmmShadowStackSize) - 1;
DEBUG ((DEBUG_INFO, "Stacks - 0x%x\n", Stacks));
DEBUG ((DEBUG_INFO, "mSmmStackSize - 0x%x\n", mSmmStackSize));
DEBUG ((DEBUG_INFO, "PcdCpuSmmStackGuard - 0x%x\n", FeaturePcdGet (PcdCpuSmmStackGuard)));
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
DEBUG ((DEBUG_INFO, "mSmmShadowStackSize - 0x%x\n", mSmmShadowStackSize));
}
//
// Initialize IDT
//
InitializeSmmIdt ();
//
// SMM Time initialization
//
InitializeSmmTimer ();
//
// Initialize MP globals
//
Cr3 = InitializeMpServiceData (Stacks, mSmmStackSize, mSmmShadowStackSize);
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) {
SetShadowStack (
Cr3,
(EFI_PHYSICAL_ADDRESS)(UINTN)Stacks + mSmmStackSize + (mSmmStackSize + mSmmShadowStackSize) * Index,
mSmmShadowStackSize
);
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
ConvertMemoryPageAttributes (
Cr3,
mPagingMode,
(EFI_PHYSICAL_ADDRESS)(UINTN)Stacks + mSmmStackSize + EFI_PAGES_TO_SIZE (1) + (mSmmStackSize + mSmmShadowStackSize) * Index,
EFI_PAGES_TO_SIZE (1),
EFI_MEMORY_RP,
TRUE,
NULL
);
}
}
}
//
// For relocated SMBASE, some MSRs & CSRs are still required to be configured in SMM Mode for SMM Initialization.
// Those MSRs & CSRs must be configured before normal SMI sources happen.
// So, here is to issue SMI IPI (All Excluding Self SMM IPI + BSP SMM IPI) to execute first SMI init.
//
ExecuteFirstSmiInit ();
//
// Call hook for BSP to perform extra actions in normal mode after all
// SMM base addresses have been relocated on all CPUs
//
SmmCpuFeaturesSmmRelocationComplete ();
DEBUG ((DEBUG_INFO, "mXdSupported - 0x%x\n", mXdSupported));
//
// Fill in SMM Reserved Regions
//
gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedStart = 0;
gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedSize = 0;
//
// Install the SMM Configuration Protocol onto a new handle on the handle database.
// The entire SMM Configuration Protocol is allocated from SMRAM, so only a pointer
// to an SMRAM address will be present in the handle database
//
Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces (
&gSmmCpuPrivate->SmmCpuHandle,
&gEfiSmmConfigurationProtocolGuid,
&gSmmCpuPrivate->SmmConfiguration,
NULL
);
ASSERT_EFI_ERROR (Status);
//
// Install the SMM CPU Protocol into SMM protocol database
//
Status = gSmst->SmmInstallProtocolInterface (
&mSmmCpuHandle,
&gEfiSmmCpuProtocolGuid,
EFI_NATIVE_INTERFACE,
&mSmmCpu
);
ASSERT_EFI_ERROR (Status);
//
// Install the SMM Memory Attribute Protocol into SMM protocol database
//
Status = gSmst->SmmInstallProtocolInterface (
&mSmmCpuHandle,
&gEdkiiSmmMemoryAttributeProtocolGuid,
EFI_NATIVE_INTERFACE,
&mSmmMemoryAttribute
);
ASSERT_EFI_ERROR (Status);
//
// Initialize global buffer for MM MP.
//
InitializeDataForMmMp ();
//
// Initialize Package First Thread Index Info.
//
InitPackageFirstThreadIndexInfo ();
//
// Install the SMM Mp Protocol into SMM protocol database
//
Status = gSmst->SmmInstallProtocolInterface (
&mSmmCpuHandle,
&gEfiMmMpProtocolGuid,
EFI_NATIVE_INTERFACE,
&mSmmMp
);
ASSERT_EFI_ERROR (Status);
//
// Expose address of CPU Hot Plug Data structure if CPU hot plug is supported.
//
if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
Status = PcdSet64S (PcdCpuHotPlugDataAddress, (UINT64)(UINTN)&mCpuHotPlugData);
ASSERT_EFI_ERROR (Status);
}
//
// Initialize SMM CPU Services Support
//
Status = InitializeSmmCpuServices (mSmmCpuHandle);
ASSERT_EFI_ERROR (Status);
//
// register SMM Ready To Lock Protocol notification
//
Status = gSmst->SmmRegisterProtocolNotify (
&gEfiSmmReadyToLockProtocolGuid,
SmmReadyToLockEventNotify,
&Registration
);
ASSERT_EFI_ERROR (Status);
//
// Initialize SMM Profile feature
//
InitSmmProfile (Cr3);
GetAcpiS3EnableFlag ();
InitSmmS3ResumeState (Cr3);
DEBUG ((DEBUG_INFO, "SMM CPU Module exit from SMRAM with EFI_SUCCESS\n"));
PERF_FUNCTION_END ();
return EFI_SUCCESS;
}
/**
Function to compare 2 EFI_SMRAM_DESCRIPTOR based on CpuStart.
@param[in] Buffer1 pointer to Device Path poiner to compare
@param[in] Buffer2 pointer to second DevicePath pointer to compare
@retval 0 Buffer1 equal to Buffer2
@retval <0 Buffer1 is less than Buffer2
@retval >0 Buffer1 is greater than Buffer2
**/
INTN
EFIAPI
CpuSmramRangeCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
)
{
if (((EFI_SMRAM_DESCRIPTOR *)Buffer1)->CpuStart > ((EFI_SMRAM_DESCRIPTOR *)Buffer2)->CpuStart) {
return 1;
} else if (((EFI_SMRAM_DESCRIPTOR *)Buffer1)->CpuStart < ((EFI_SMRAM_DESCRIPTOR *)Buffer2)->CpuStart) {
return -1;
}
return 0;
}
/**
Find out SMRAM information including SMRR base and SMRR size.
@param SmrrBase SMRR base
@param SmrrSize SMRR size
**/
VOID
FindSmramInfo (
OUT UINT32 *SmrrBase,
OUT UINT32 *SmrrSize
)
{
EFI_STATUS Status;
UINTN Size;
EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
EFI_SMRAM_DESCRIPTOR *CurrentSmramRange;
UINTN Index;
UINT64 MaxSize;
BOOLEAN Found;
EFI_SMRAM_DESCRIPTOR SmramDescriptor;
//
// Get SMM Access Protocol
//
Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
ASSERT_EFI_ERROR (Status);
//
// Get SMRAM information
//
Size = 0;
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
mSmmCpuSmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size);
ASSERT (mSmmCpuSmramRanges != NULL);
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmCpuSmramRanges);
ASSERT_EFI_ERROR (Status);
mSmmCpuSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
//
// Sort the mSmmCpuSmramRanges
//
QuickSort (mSmmCpuSmramRanges, mSmmCpuSmramRangeCount, sizeof (EFI_SMRAM_DESCRIPTOR), (BASE_SORT_COMPARE)CpuSmramRangeCompare, &SmramDescriptor);
//
// Find the largest SMRAM range between 1MB and 4GB that is at least 256K - 4K in size
//
CurrentSmramRange = NULL;
for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < mSmmCpuSmramRangeCount; Index++) {
//
// Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
//
if ((mSmmCpuSmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
continue;
}
if (mSmmCpuSmramRanges[Index].CpuStart >= BASE_1MB) {
if ((mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize) <= SMRR_MAX_ADDRESS) {
if (mSmmCpuSmramRanges[Index].PhysicalSize >= MaxSize) {
MaxSize = mSmmCpuSmramRanges[Index].PhysicalSize;
CurrentSmramRange = &mSmmCpuSmramRanges[Index];
}
}
}
}
ASSERT (CurrentSmramRange != NULL);
*SmrrBase = (UINT32)CurrentSmramRange->CpuStart;
*SmrrSize = (UINT32)CurrentSmramRange->PhysicalSize;
do {
Found = FALSE;
for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
if ((mSmmCpuSmramRanges[Index].CpuStart < *SmrrBase) &&
(*SmrrBase == (mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize)))
{
*SmrrBase = (UINT32)mSmmCpuSmramRanges[Index].CpuStart;
*SmrrSize = (UINT32)(*SmrrSize + mSmmCpuSmramRanges[Index].PhysicalSize);
Found = TRUE;
} else if (((*SmrrBase + *SmrrSize) == mSmmCpuSmramRanges[Index].CpuStart) && (mSmmCpuSmramRanges[Index].PhysicalSize > 0)) {
*SmrrSize = (UINT32)(*SmrrSize + mSmmCpuSmramRanges[Index].PhysicalSize);
Found = TRUE;
}
}
} while (Found);
DEBUG ((DEBUG_INFO, "SMRR Base: 0x%x, SMRR Size: 0x%x\n", *SmrrBase, *SmrrSize));
}
/**
Configure SMM Code Access Check feature on an AP.
SMM Feature Control MSR will be locked after configuration.
@param[in,out] Buffer Pointer to private data buffer.
**/
VOID
EFIAPI
ConfigSmmCodeAccessCheckOnCurrentProcessor (
IN OUT VOID *Buffer
)
{
UINTN CpuIndex;
UINT64 SmmFeatureControlMsr;
UINT64 NewSmmFeatureControlMsr;
//
// Retrieve the CPU Index from the context passed in
//
CpuIndex = *(UINTN *)Buffer;
//
// Get the current SMM Feature Control MSR value
//
SmmFeatureControlMsr = SmmCpuFeaturesGetSmmRegister (CpuIndex, SmmRegFeatureControl);
//
// Compute the new SMM Feature Control MSR value
//
NewSmmFeatureControlMsr = SmmFeatureControlMsr;
if (mSmmCodeAccessCheckEnable) {
NewSmmFeatureControlMsr |= SMM_CODE_CHK_EN_BIT;
if (FeaturePcdGet (PcdCpuSmmFeatureControlMsrLock)) {
NewSmmFeatureControlMsr |= SMM_FEATURE_CONTROL_LOCK_BIT;
}
}
//
// Only set the SMM Feature Control MSR value if the new value is different than the current value
//
if (NewSmmFeatureControlMsr != SmmFeatureControlMsr) {
SmmCpuFeaturesSetSmmRegister (CpuIndex, SmmRegFeatureControl, NewSmmFeatureControlMsr);
}
//
// Release the spin lock user to serialize the updates to the SMM Feature Control MSR
//
ReleaseSpinLock (mConfigSmmCodeAccessCheckLock);
}
/**
Configure SMM Code Access Check feature for all processors.
SMM Feature Control MSR will be locked after configuration.
**/
VOID
ConfigSmmCodeAccessCheck (
VOID
)
{
UINTN Index;
EFI_STATUS Status;
PERF_FUNCTION_BEGIN ();
//
// Check to see if the Feature Control MSR is supported on this CPU
//
Index = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu;
if (!SmmCpuFeaturesIsSmmRegisterSupported (Index, SmmRegFeatureControl)) {
mSmmCodeAccessCheckEnable = FALSE;
PERF_FUNCTION_END ();
return;
}
//
// Check to see if the CPU supports the SMM Code Access Check feature
// Do not access this MSR unless the CPU supports the SmmRegFeatureControl
//
if ((AsmReadMsr64 (EFI_MSR_SMM_MCA_CAP) & SMM_CODE_ACCESS_CHK_BIT) == 0) {
mSmmCodeAccessCheckEnable = FALSE;
PERF_FUNCTION_END ();
return;
}
//
// Initialize the lock used to serialize the MSR programming in BSP and all APs
//
InitializeSpinLock (mConfigSmmCodeAccessCheckLock);
//
// Acquire Config SMM Code Access Check spin lock. The BSP will release the
// spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
//
AcquireSpinLock (mConfigSmmCodeAccessCheckLock);
//
// Enable SMM Code Access Check feature on the BSP.
//
ConfigSmmCodeAccessCheckOnCurrentProcessor (&Index);
//
// Enable SMM Code Access Check feature for the APs.
//
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
if (Index != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {
if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) {
//
// If this processor does not exist
//
continue;
}
//
// Acquire Config SMM Code Access Check spin lock. The AP will release the
// spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor().
//
AcquireSpinLock (mConfigSmmCodeAccessCheckLock);
//
// Call SmmStartupThisAp() to enable SMM Code Access Check on an AP.
//
Status = gSmst->SmmStartupThisAp (ConfigSmmCodeAccessCheckOnCurrentProcessor, Index, &Index);
ASSERT_EFI_ERROR (Status);
//
// Wait for the AP to release the Config SMM Code Access Check spin lock.
//
while (!AcquireSpinLockOrFail (mConfigSmmCodeAccessCheckLock)) {
CpuPause ();
}
//
// Release the Config SMM Code Access Check spin lock.
//
ReleaseSpinLock (mConfigSmmCodeAccessCheckLock);
}
}
PERF_FUNCTION_END ();
}
/**
Allocate pages for code.
@param[in] Pages Number of pages to be allocated.
@return Allocated memory.
**/
VOID *
AllocateCodePages (
IN UINTN Pages
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Memory;
if (Pages == 0) {
return NULL;
}
Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory);
if (EFI_ERROR (Status)) {
return NULL;
}
return (VOID *)(UINTN)Memory;
}
/**
Perform the remaining tasks.
**/
VOID
PerformRemainingTasks (
VOID
)
{
if (mSmmReadyToLock) {
PERF_FUNCTION_BEGIN ();
//
// Check if all Aps enter SMM. In Relaxed-AP Sync Mode, BSP will not wait for
// all Aps arrive. However,PerformRemainingTasks() needs to wait all Aps arrive before calling
// SetMemMapAttributes() and ConfigSmmCodeAccessCheck() when mSmmReadyToLock
// is true. In SetMemMapAttributes(), SmmSetMemoryAttributesEx() will call
// FlushTlbForAll() that need to start up the aps. So it need to let all
// aps arrive. Same as SetMemMapAttributes(), ConfigSmmCodeAccessCheck()
// also will start up the aps.
//
if (EFI_ERROR (SmmCpuRendezvous (NULL, TRUE))) {
DEBUG ((DEBUG_ERROR, "PerformRemainingTasks: fail to wait for all AP check in SMM!\n"));
}
//
// Start SMM Profile feature
//
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
SmmProfileStart ();
}
//
// Create a mix of 2MB and 4KB page table. Update some memory ranges absent and execute-disable.
//
InitPaging ();
//
// Mark critical region to be read-only in page table
//
SetMemMapAttributes ();
if (IsRestrictedMemoryAccess ()) {
//
// For outside SMRAM, we only map SMM communication buffer or MMIO.
//
SetUefiMemMapAttributes ();
//
// Set page table itself to be read-only
//
SetPageTableAttributes ();
}
//
// Configure SMM Code Access Check feature if available.
//
ConfigSmmCodeAccessCheck ();
//
// Measure performance of SmmCpuFeaturesCompleteSmmReadyToLock() from caller side
// as the implementation is provided by platform.
//
PERF_START (NULL, "SmmCompleteReadyToLock", NULL, 0);
SmmCpuFeaturesCompleteSmmReadyToLock ();
PERF_END (NULL, "SmmCompleteReadyToLock", NULL, 0);
//
// Clean SMM ready to lock flag
//
mSmmReadyToLock = FALSE;
PERF_FUNCTION_END ();
}
}
/**
Perform the pre tasks.
**/
VOID
PerformPreTasks (
VOID
)
{
RestoreSmmConfigurationInS3 ();
}