/** @file | |
SMM STM support functions | |
Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiMm.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/HobLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/SmmServicesTableLib.h> | |
#include <Library/TpmMeasurementLib.h> | |
#include <Register/Intel/Cpuid.h> | |
#include <Register/Intel/ArchitecturalMsr.h> | |
#include <Register/Intel/SmramSaveStateMap.h> | |
#include <Protocol/MpService.h> | |
#include "CpuFeaturesLib.h" | |
#include "SmmStm.h" | |
#define TXT_EVTYPE_BASE 0x400 | |
#define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14) | |
#define RDWR_ACCS 3 | |
#define FULL_ACCS 7 | |
EFI_HANDLE mStmSmmCpuHandle = NULL; | |
BOOLEAN mLockLoadMonitor = FALSE; | |
// | |
// Template of STM_RSC_END structure for copying. | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED STM_RSC_END mRscEndNode = { | |
{ END_OF_RESOURCES, sizeof (STM_RSC_END) }, | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 *mStmResourcesPtr = NULL; | |
GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceTotalSize = 0x0; | |
GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeUsed = 0x0; | |
GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeAvailable = 0x0; | |
GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mStmState = 0; | |
// | |
// System Configuration Table pointing to STM Configuration Table | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED | |
EFI_SM_MONITOR_INIT_PROTOCOL mSmMonitorInitProtocol = { | |
LoadMonitor, | |
AddPiResource, | |
DeletePiResource, | |
GetPiResource, | |
GetMonitorState, | |
}; | |
#define CPUID1_EDX_XD_SUPPORT 0x100000 | |
// | |
// External global variables associated with SMI Handler Template | |
// | |
extern CONST TXT_PROCESSOR_SMM_DESCRIPTOR gcStmPsd; | |
extern UINT32 gStmSmbase; | |
extern volatile UINT32 gStmSmiStack; | |
extern UINT32 gStmSmiCr3; | |
extern volatile UINT8 gcStmSmiHandlerTemplate[]; | |
extern CONST UINT16 gcStmSmiHandlerSize; | |
extern UINT16 gcStmSmiHandlerOffset; | |
extern BOOLEAN gStmXdSupported; | |
// | |
// Variables used by SMI Handler | |
// | |
IA32_DESCRIPTOR gStmSmiHandlerIdtr; | |
// | |
// MP Services Protocol | |
// | |
EFI_MP_SERVICES_PROTOCOL *mSmmCpuFeaturesLibMpService = NULL; | |
// | |
// MSEG Base and Length in SMRAM | |
// | |
UINTN mMsegBase = 0; | |
UINTN mMsegSize = 0; | |
BOOLEAN mStmConfigurationTableInitialized = FALSE; | |
/** | |
The constructor function for the Traditional MM library instance with STM. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmCpuFeaturesLibStmConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
CPUID_VERSION_INFO_ECX RegEcx; | |
EFI_HOB_GUID_TYPE *GuidHob; | |
EFI_SMRAM_DESCRIPTOR *SmramDescriptor; | |
// | |
// Initialize address fixup | |
// | |
SmmCpuFeaturesLibStmSmiEntryFixupAddress (); | |
// | |
// Perform library initialization common across all instances | |
// | |
CpuFeaturesLibInitialization (); | |
// | |
// Lookup the MP Services Protocol | |
// | |
Status = gBS->LocateProtocol ( | |
&gEfiMpServiceProtocolGuid, | |
NULL, | |
(VOID **)&mSmmCpuFeaturesLibMpService | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// If CPU supports VMX, then determine SMRAM range for MSEG. | |
// | |
AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx.Uint32, NULL); | |
if (RegEcx.Bits.VMX == 1) { | |
GuidHob = GetFirstGuidHob (&gMsegSmramGuid); | |
if (GuidHob != NULL) { | |
// | |
// Retrieve MSEG location from MSEG SRAM HOB | |
// | |
SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *)GET_GUID_HOB_DATA (GuidHob); | |
if (SmramDescriptor->PhysicalSize > 0) { | |
mMsegBase = (UINTN)SmramDescriptor->CpuStart; | |
mMsegSize = (UINTN)SmramDescriptor->PhysicalSize; | |
} | |
} else if (PcdGet32 (PcdCpuMsegSize) > 0) { | |
// | |
// Allocate MSEG from SMRAM memory | |
// | |
mMsegBase = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuMsegSize))); | |
if (mMsegBase > 0) { | |
mMsegSize = ALIGN_VALUE (PcdGet32 (PcdCpuMsegSize), EFI_PAGE_SIZE); | |
} else { | |
DEBUG ((DEBUG_ERROR, "Not enough SMRAM resource to allocate MSEG size %08x\n", PcdGet32 (PcdCpuMsegSize))); | |
} | |
} | |
if (mMsegBase > 0) { | |
DEBUG ((DEBUG_INFO, "MsegBase: 0x%08x, MsegSize: 0x%08x\n", mMsegBase, mMsegSize)); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Internal worker function that is called to complete CPU initialization at the | |
end of SmmCpuFeaturesInitializeProcessor(). | |
**/ | |
VOID | |
FinishSmmCpuFeaturesInitializeProcessor ( | |
VOID | |
) | |
{ | |
MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; | |
// | |
// Set MSEG Base Address in SMM Monitor Control MSR. | |
// | |
if (mMsegBase > 0) { | |
SmmMonitorCtl.Uint64 = 0; | |
SmmMonitorCtl.Bits.MsegBase = (UINT32)mMsegBase >> 12; | |
SmmMonitorCtl.Bits.Valid = 1; | |
AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); | |
} | |
} | |
/** | |
Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is | |
returned, then a custom SMI handler is not provided by this library, | |
and the default SMI handler must be used. | |
@retval 0 Use the default SMI handler. | |
@retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() | |
The caller is required to allocate enough SMRAM for each CPU to | |
support the size of the custom SMI handler. | |
**/ | |
UINTN | |
EFIAPI | |
SmmCpuFeaturesGetSmiHandlerSize ( | |
VOID | |
) | |
{ | |
return gcStmSmiHandlerSize; | |
} | |
/** | |
Install a custom SMI handler for the CPU specified by CpuIndex. This function | |
is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater | |
than zero and is called by the CPU that was elected as monarch during System | |
Management Mode initialization. | |
@param[in] CpuIndex The index of the CPU to install the custom SMI handler. | |
The value must be between 0 and the NumberOfCpus field | |
in the System Management System Table (SMST). | |
@param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. | |
@param[in] SmiStack The stack to use when an SMI is processed by the | |
the CPU specified by CpuIndex. | |
@param[in] StackSize The size, in bytes, if the stack used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] GdtBase The base address of the GDT to use when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] GdtSize The size, in bytes, of the GDT used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] IdtBase The base address of the IDT to use when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] IdtSize The size, in bytes, of the IDT used when an SMI is | |
processed by the CPU specified by CpuIndex. | |
@param[in] Cr3 The base address of the page tables to use when an SMI | |
is processed by the CPU specified by CpuIndex. | |
**/ | |
VOID | |
EFIAPI | |
SmmCpuFeaturesInstallSmiHandler ( | |
IN UINTN CpuIndex, | |
IN UINT32 SmBase, | |
IN VOID *SmiStack, | |
IN UINTN StackSize, | |
IN UINTN GdtBase, | |
IN UINTN GdtSize, | |
IN UINTN IdtBase, | |
IN UINTN IdtSize, | |
IN UINT32 Cr3 | |
) | |
{ | |
EFI_STATUS Status; | |
TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; | |
VOID *Hob; | |
UINT32 RegEax; | |
UINT32 RegEdx; | |
EFI_PROCESSOR_INFORMATION ProcessorInfo; | |
CopyMem ((VOID *)((UINTN)SmBase + TXT_SMM_PSD_OFFSET), &gcStmPsd, sizeof (gcStmPsd)); | |
Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)(VOID *)((UINTN)SmBase + TXT_SMM_PSD_OFFSET); | |
Psd->SmmGdtPtr = GdtBase; | |
Psd->SmmGdtSize = (UINT32)GdtSize; | |
// | |
// Initialize values in template before copy | |
// | |
gStmSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); | |
gStmSmiCr3 = Cr3; | |
gStmSmbase = SmBase; | |
gStmSmiHandlerIdtr.Base = IdtBase; | |
gStmSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); | |
if (gStmXdSupported) { | |
AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); | |
if (RegEax <= CPUID_EXTENDED_FUNCTION) { | |
// | |
// Extended CPUID functions are not supported on this processor. | |
// | |
gStmXdSupported = FALSE; | |
} | |
AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); | |
if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) { | |
// | |
// Execute Disable Bit feature is not supported on this processor. | |
// | |
gStmXdSupported = FALSE; | |
} | |
} | |
// | |
// Set the value at the top of the CPU stack to the CPU Index | |
// | |
*(UINTN *)(UINTN)gStmSmiStack = CpuIndex; | |
// | |
// Copy template to CPU specific SMI handler location | |
// | |
CopyMem ( | |
(VOID *)((UINTN)SmBase + SMM_HANDLER_OFFSET), | |
(VOID *)gcStmSmiHandlerTemplate, | |
gcStmSmiHandlerSize | |
); | |
Psd->SmmSmiHandlerRip = SmBase + SMM_HANDLER_OFFSET + gcStmSmiHandlerOffset; | |
Psd->SmmSmiHandlerRsp = (UINTN)SmiStack + StackSize - sizeof (UINTN); | |
Psd->SmmCr3 = Cr3; | |
DEBUG ((DEBUG_INFO, "CpuSmmStmExceptionStackSize - %x\n", PcdGet32 (PcdCpuSmmStmExceptionStackSize))); | |
DEBUG ((DEBUG_INFO, "Pages - %x\n", EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize)))); | |
Psd->StmProtectionExceptionHandler.SpeRsp = (UINT64)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); | |
Psd->StmProtectionExceptionHandler.SpeRsp += EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); | |
Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)GetStmResource (); | |
// | |
// Get the APIC ID for the CPU specified by CpuIndex | |
// | |
Status = mSmmCpuFeaturesLibMpService->GetProcessorInfo ( | |
mSmmCpuFeaturesLibMpService, | |
CpuIndex, | |
&ProcessorInfo | |
); | |
ASSERT_EFI_ERROR (Status); | |
Psd->LocalApicId = (UINT32)ProcessorInfo.ProcessorId; | |
Psd->AcpiRsdp = 0; | |
Hob = GetFirstHob (EFI_HOB_TYPE_CPU); | |
if (Hob != NULL) { | |
Psd->PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace; | |
} else { | |
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); | |
if (RegEax >= 0x80000008) { | |
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); | |
Psd->PhysicalAddressBits = (UINT8)RegEax; | |
} else { | |
Psd->PhysicalAddressBits = 36; | |
} | |
} | |
if (!mStmConfigurationTableInitialized) { | |
StmSmmConfigurationTableInit (); | |
mStmConfigurationTableInitialized = TRUE; | |
} | |
} | |
/** | |
SMM End Of Dxe event notification handler. | |
STM support need patch AcpiRsdp in TXT_PROCESSOR_SMM_DESCRIPTOR. | |
@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 | |
SmmEndOfDxeEventNotify ( | |
IN CONST EFI_GUID *Protocol, | |
IN VOID *Interface, | |
IN EFI_HANDLE Handle | |
) | |
{ | |
VOID *Rsdp; | |
UINTN Index; | |
TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; | |
DEBUG ((DEBUG_INFO, "SmmEndOfDxeEventNotify\n")); | |
// | |
// found ACPI table RSD_PTR from system table | |
// | |
Rsdp = NULL; | |
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { | |
if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid)) { | |
// | |
// A match was found. | |
// | |
Rsdp = gST->ConfigurationTable[Index].VendorTable; | |
break; | |
} | |
} | |
if (Rsdp == NULL) { | |
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { | |
if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid)) { | |
// | |
// A match was found. | |
// | |
Rsdp = gST->ConfigurationTable[Index].VendorTable; | |
break; | |
} | |
} | |
} | |
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { | |
Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); | |
DEBUG ((DEBUG_INFO, "Index=%d Psd=%p Rsdp=%p\n", Index, Psd, Rsdp)); | |
Psd->AcpiRsdp = (UINT64)(UINTN)Rsdp; | |
} | |
mLockLoadMonitor = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
This function initializes the STM configuration table. | |
**/ | |
VOID | |
StmSmmConfigurationTableInit ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
VOID *Registration; | |
Status = gSmst->SmmInstallProtocolInterface ( | |
&mStmSmmCpuHandle, | |
&gEfiSmMonitorInitProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mSmMonitorInitProtocol | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// | |
// Register SMM End of DXE Event | |
// | |
Status = gSmst->SmmRegisterProtocolNotify ( | |
&gEfiSmmEndOfDxeProtocolGuid, | |
SmmEndOfDxeEventNotify, | |
&Registration | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
/** | |
Get STM state. | |
@return STM state | |
**/ | |
EFI_SM_MONITOR_STATE | |
EFIAPI | |
GetMonitorState ( | |
VOID | |
) | |
{ | |
return mStmState; | |
} | |
/** | |
Handle single Resource to see if it can be merged into Record. | |
@param Resource A pointer to resource node to be added | |
@param Record A pointer to record node to be merged | |
@retval TRUE resource handled | |
@retval FALSE resource is not handled | |
**/ | |
BOOLEAN | |
HandleSingleResource ( | |
IN STM_RSC *Resource, | |
IN STM_RSC *Record | |
) | |
{ | |
UINT64 ResourceLo; | |
UINT64 ResourceHi; | |
UINT64 RecordLo; | |
UINT64 RecordHi; | |
ResourceLo = 0; | |
ResourceHi = 0; | |
RecordLo = 0; | |
RecordHi = 0; | |
// | |
// Calling code is responsible for making sure that | |
// Resource->Header.RscType == (*Record)->Header.RscType | |
// thus we use just one of them as switch variable. | |
// | |
switch (Resource->Header.RscType) { | |
case MEM_RANGE: | |
case MMIO_RANGE: | |
ResourceLo = Resource->Mem.Base; | |
ResourceHi = Resource->Mem.Base + Resource->Mem.Length; | |
RecordLo = Record->Mem.Base; | |
RecordHi = Record->Mem.Base + Record->Mem.Length; | |
if (Resource->Mem.RWXAttributes != Record->Mem.RWXAttributes) { | |
if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { | |
Record->Mem.RWXAttributes = Resource->Mem.RWXAttributes | Record->Mem.RWXAttributes; | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
break; | |
case IO_RANGE: | |
case TRAPPED_IO_RANGE: | |
ResourceLo = (UINT64)Resource->Io.Base; | |
ResourceHi = (UINT64)Resource->Io.Base + (UINT64)Resource->Io.Length; | |
RecordLo = (UINT64)Record->Io.Base; | |
RecordHi = (UINT64)Record->Io.Base + (UINT64)Record->Io.Length; | |
break; | |
case PCI_CFG_RANGE: | |
if ((Resource->PciCfg.OriginatingBusNumber != Record->PciCfg.OriginatingBusNumber) || | |
(Resource->PciCfg.LastNodeIndex != Record->PciCfg.LastNodeIndex)) | |
{ | |
return FALSE; | |
} | |
if (CompareMem (Resource->PciCfg.PciDevicePath, Record->PciCfg.PciDevicePath, sizeof (STM_PCI_DEVICE_PATH_NODE) * (Resource->PciCfg.LastNodeIndex + 1)) != 0) { | |
return FALSE; | |
} | |
ResourceLo = (UINT64)Resource->PciCfg.Base; | |
ResourceHi = (UINT64)Resource->PciCfg.Base + (UINT64)Resource->PciCfg.Length; | |
RecordLo = (UINT64)Record->PciCfg.Base; | |
RecordHi = (UINT64)Record->PciCfg.Base + (UINT64)Record->PciCfg.Length; | |
if (Resource->PciCfg.RWAttributes != Record->PciCfg.RWAttributes) { | |
if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { | |
Record->PciCfg.RWAttributes = Resource->PciCfg.RWAttributes | Record->PciCfg.RWAttributes; | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
break; | |
case MACHINE_SPECIFIC_REG: | |
// | |
// Special case - merge MSR masks in place. | |
// | |
if (Resource->Msr.MsrIndex != Record->Msr.MsrIndex) { | |
return FALSE; | |
} | |
Record->Msr.ReadMask |= Resource->Msr.ReadMask; | |
Record->Msr.WriteMask |= Resource->Msr.WriteMask; | |
return TRUE; | |
default: | |
return FALSE; | |
} | |
// | |
// If resources are disjoint | |
// | |
if ((ResourceHi < RecordLo) || (ResourceLo > RecordHi)) { | |
return FALSE; | |
} | |
// | |
// If resource is consumed by record. | |
// | |
if ((ResourceLo >= RecordLo) && (ResourceHi <= RecordHi)) { | |
return TRUE; | |
} | |
// | |
// Resources are overlapping. | |
// Resource and record are merged. | |
// | |
ResourceLo = (ResourceLo < RecordLo) ? ResourceLo : RecordLo; | |
ResourceHi = (ResourceHi > RecordHi) ? ResourceHi : RecordHi; | |
switch (Resource->Header.RscType) { | |
case MEM_RANGE: | |
case MMIO_RANGE: | |
Record->Mem.Base = ResourceLo; | |
Record->Mem.Length = ResourceHi - ResourceLo; | |
break; | |
case IO_RANGE: | |
case TRAPPED_IO_RANGE: | |
Record->Io.Base = (UINT16)ResourceLo; | |
Record->Io.Length = (UINT16)(ResourceHi - ResourceLo); | |
break; | |
case PCI_CFG_RANGE: | |
Record->PciCfg.Base = (UINT16)ResourceLo; | |
Record->PciCfg.Length = (UINT16)(ResourceHi - ResourceLo); | |
break; | |
default: | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Add resource node. | |
@param Resource A pointer to resource node to be added | |
**/ | |
VOID | |
AddSingleResource ( | |
IN STM_RSC *Resource | |
) | |
{ | |
STM_RSC *Record; | |
Record = (STM_RSC *)mStmResourcesPtr; | |
while (TRUE) { | |
if (Record->Header.RscType == END_OF_RESOURCES) { | |
break; | |
} | |
// | |
// Go to next record if resource and record types don't match. | |
// | |
if (Resource->Header.RscType != Record->Header.RscType) { | |
Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); | |
continue; | |
} | |
// | |
// Record is handled inside of procedure - don't adjust. | |
// | |
if (HandleSingleResource (Resource, Record)) { | |
return; | |
} | |
Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); | |
} | |
// | |
// Add resource to the end of area. | |
// | |
CopyMem ( | |
mStmResourcesPtr + mStmResourceSizeUsed - sizeof (mRscEndNode), | |
Resource, | |
Resource->Header.Length | |
); | |
CopyMem ( | |
mStmResourcesPtr + mStmResourceSizeUsed - sizeof (mRscEndNode) + Resource->Header.Length, | |
&mRscEndNode, | |
sizeof (mRscEndNode) | |
); | |
mStmResourceSizeUsed += Resource->Header.Length; | |
mStmResourceSizeAvailable = mStmResourceTotalSize - mStmResourceSizeUsed; | |
return; | |
} | |
/** | |
Add resource list. | |
@param ResourceList A pointer to resource list to be added | |
@param NumEntries Optional number of entries. | |
If 0, list must be terminated by END_OF_RESOURCES. | |
**/ | |
VOID | |
AddResource ( | |
IN STM_RSC *ResourceList, | |
IN UINT32 NumEntries OPTIONAL | |
) | |
{ | |
UINT32 Count; | |
UINTN Index; | |
STM_RSC *Resource; | |
if (NumEntries == 0) { | |
Count = 0xFFFFFFFF; | |
} else { | |
Count = NumEntries; | |
} | |
Resource = ResourceList; | |
for (Index = 0; Index < Count; Index++) { | |
if (Resource->Header.RscType == END_OF_RESOURCES) { | |
return; | |
} | |
AddSingleResource (Resource); | |
Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); | |
} | |
return; | |
} | |
/** | |
Validate resource list. | |
@param ResourceList A pointer to resource list to be added | |
@param NumEntries Optional number of entries. | |
If 0, list must be terminated by END_OF_RESOURCES. | |
@retval TRUE resource valid | |
@retval FALSE resource invalid | |
**/ | |
BOOLEAN | |
ValidateResource ( | |
IN STM_RSC *ResourceList, | |
IN UINT32 NumEntries OPTIONAL | |
) | |
{ | |
UINT32 Count; | |
UINTN Index; | |
STM_RSC *Resource; | |
UINTN SubIndex; | |
// | |
// If NumEntries == 0 make it very big. Scan will be terminated by | |
// END_OF_RESOURCES. | |
// | |
if (NumEntries == 0) { | |
Count = 0xFFFFFFFF; | |
} else { | |
Count = NumEntries; | |
} | |
// | |
// Start from beginning of resource list. | |
// | |
Resource = ResourceList; | |
for (Index = 0; Index < Count; Index++) { | |
DEBUG ((DEBUG_INFO, "ValidateResource (%d) - RscType(%x)\n", Index, Resource->Header.RscType)); | |
// | |
// Validate resource. | |
// | |
switch (Resource->Header.RscType) { | |
case END_OF_RESOURCES: | |
if (Resource->Header.Length != sizeof (STM_RSC_END)) { | |
return FALSE; | |
} | |
// | |
// If we are passed actual number of resources to add, | |
// END_OF_RESOURCES structure between them is considered an | |
// error. If NumEntries == 0 END_OF_RESOURCES is a termination. | |
// | |
if (NumEntries != 0) { | |
return FALSE; | |
} else { | |
// | |
// If NumEntries == 0 and list reached end - return success. | |
// | |
return TRUE; | |
} | |
break; | |
case MEM_RANGE: | |
case MMIO_RANGE: | |
if (Resource->Header.Length != sizeof (STM_RSC_MEM_DESC)) { | |
return FALSE; | |
} | |
if (Resource->Mem.RWXAttributes > FULL_ACCS) { | |
return FALSE; | |
} | |
break; | |
case IO_RANGE: | |
case TRAPPED_IO_RANGE: | |
if (Resource->Header.Length != sizeof (STM_RSC_IO_DESC)) { | |
return FALSE; | |
} | |
if ((Resource->Io.Base + Resource->Io.Length) > 0xFFFF) { | |
return FALSE; | |
} | |
break; | |
case PCI_CFG_RANGE: | |
DEBUG ((DEBUG_INFO, "ValidateResource - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n", Resource->PciCfg.OriginatingBusNumber, Resource->PciCfg.LastNodeIndex, Resource->PciCfg.PciDevicePath[0].PciDevice, Resource->PciCfg.PciDevicePath[0].PciFunction)); | |
if (Resource->Header.Length != sizeof (STM_RSC_PCI_CFG_DESC) + (sizeof (STM_PCI_DEVICE_PATH_NODE) * Resource->PciCfg.LastNodeIndex)) { | |
return FALSE; | |
} | |
for (SubIndex = 0; SubIndex <= Resource->PciCfg.LastNodeIndex; SubIndex++) { | |
if ((Resource->PciCfg.PciDevicePath[SubIndex].PciDevice > 0x1F) || (Resource->PciCfg.PciDevicePath[SubIndex].PciFunction > 7)) { | |
return FALSE; | |
} | |
} | |
if ((Resource->PciCfg.Base + Resource->PciCfg.Length) > 0x1000) { | |
return FALSE; | |
} | |
break; | |
case MACHINE_SPECIFIC_REG: | |
if (Resource->Header.Length != sizeof (STM_RSC_MSR_DESC)) { | |
return FALSE; | |
} | |
break; | |
default: | |
DEBUG ((DEBUG_ERROR, "ValidateResource - Unknown RscType(%x)\n", Resource->Header.RscType)); | |
return FALSE; | |
} | |
Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); | |
} | |
return TRUE; | |
} | |
/** | |
Get resource list. | |
EndResource is excluded. | |
@param ResourceList A pointer to resource list to be added | |
@param NumEntries Optional number of entries. | |
If 0, list must be terminated by END_OF_RESOURCES. | |
@retval TRUE resource valid | |
@retval FALSE resource invalid | |
**/ | |
UINTN | |
GetResourceSize ( | |
IN STM_RSC *ResourceList, | |
IN UINT32 NumEntries OPTIONAL | |
) | |
{ | |
UINT32 Count; | |
UINTN Index; | |
STM_RSC *Resource; | |
Resource = ResourceList; | |
// | |
// If NumEntries == 0 make it very big. Scan will be terminated by | |
// END_OF_RESOURCES. | |
// | |
if (NumEntries == 0) { | |
Count = 0xFFFFFFFF; | |
} else { | |
Count = NumEntries; | |
} | |
// | |
// Start from beginning of resource list. | |
// | |
Resource = ResourceList; | |
for (Index = 0; Index < Count; Index++) { | |
if (Resource->Header.RscType == END_OF_RESOURCES) { | |
break; | |
} | |
Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); | |
} | |
return (UINTN)Resource - (UINTN)ResourceList; | |
} | |
/** | |
Add resources in list to database. Allocate new memory areas as needed. | |
@param ResourceList A pointer to resource list to be added | |
@param NumEntries Optional number of entries. | |
If 0, list must be terminated by END_OF_RESOURCES. | |
@retval EFI_SUCCESS If resources are added | |
@retval EFI_INVALID_PARAMETER If nested procedure detected resource failer | |
@retval EFI_OUT_OF_RESOURCES If nested procedure returned it and we cannot allocate more areas. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AddPiResource ( | |
IN STM_RSC *ResourceList, | |
IN UINT32 NumEntries OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN ResourceSize; | |
EFI_PHYSICAL_ADDRESS NewResource; | |
UINTN NewResourceSize; | |
DEBUG ((DEBUG_INFO, "AddPiResource - Enter\n")); | |
if (!ValidateResource (ResourceList, NumEntries)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ResourceSize = GetResourceSize (ResourceList, NumEntries); | |
DEBUG ((DEBUG_INFO, "ResourceSize - 0x%08x\n", ResourceSize)); | |
if (ResourceSize == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (mStmResourcesPtr == NULL) { | |
// | |
// First time allocation | |
// | |
NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ResourceSize + sizeof (mRscEndNode))); | |
DEBUG ((DEBUG_INFO, "Allocate - 0x%08x\n", NewResourceSize)); | |
Status = gSmst->SmmAllocatePages ( | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (NewResourceSize), | |
&NewResource | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Copy EndResource for initialization | |
// | |
mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; | |
mStmResourceTotalSize = NewResourceSize; | |
CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof (mRscEndNode)); | |
mStmResourceSizeUsed = sizeof (mRscEndNode); | |
mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof (mRscEndNode); | |
// | |
// Let SmmCore change resource ptr | |
// | |
NotifyStmResourceChange (mStmResourcesPtr); | |
} else if (mStmResourceSizeAvailable < ResourceSize) { | |
// | |
// Need enlarge | |
// | |
NewResourceSize = mStmResourceTotalSize + (ResourceSize - mStmResourceSizeAvailable); | |
NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (NewResourceSize)); | |
DEBUG ((DEBUG_INFO, "ReAllocate - 0x%08x\n", NewResourceSize)); | |
Status = gSmst->SmmAllocatePages ( | |
AllocateAnyPages, | |
EfiRuntimeServicesData, | |
EFI_SIZE_TO_PAGES (NewResourceSize), | |
&NewResource | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
CopyMem ((VOID *)(UINTN)NewResource, mStmResourcesPtr, mStmResourceSizeUsed); | |
mStmResourceSizeAvailable = NewResourceSize - mStmResourceSizeUsed; | |
gSmst->SmmFreePages ( | |
(EFI_PHYSICAL_ADDRESS)(UINTN)mStmResourcesPtr, | |
EFI_SIZE_TO_PAGES (mStmResourceTotalSize) | |
); | |
mStmResourceTotalSize = NewResourceSize; | |
mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; | |
// | |
// Let SmmCore change resource ptr | |
// | |
NotifyStmResourceChange (mStmResourcesPtr); | |
} | |
// | |
// Check duplication | |
// | |
AddResource (ResourceList, NumEntries); | |
return EFI_SUCCESS; | |
} | |
/** | |
Delete resources in list to database. | |
@param ResourceList A pointer to resource list to be deleted | |
NULL means delete all resources. | |
@param NumEntries Optional number of entries. | |
If 0, list must be terminated by END_OF_RESOURCES. | |
@retval EFI_SUCCESS If resources are deleted | |
@retval EFI_INVALID_PARAMETER If nested procedure detected resource failer | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DeletePiResource ( | |
IN STM_RSC *ResourceList, | |
IN UINT32 NumEntries OPTIONAL | |
) | |
{ | |
if (ResourceList != NULL) { | |
// TBD | |
ASSERT (FALSE); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Delete all | |
// | |
CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof (mRscEndNode)); | |
mStmResourceSizeUsed = sizeof (mRscEndNode); | |
mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof (mRscEndNode); | |
return EFI_SUCCESS; | |
} | |
/** | |
Get BIOS resources. | |
@param ResourceList A pointer to resource list to be filled | |
@param ResourceSize On input it means size of resource list input. | |
On output it means size of resource list filled, | |
or the size of resource list to be filled if size of too small. | |
@retval EFI_SUCCESS If resources are returned. | |
@retval EFI_BUFFER_TOO_SMALL If resource list buffer is too small to hold the whole resources. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetPiResource ( | |
OUT STM_RSC *ResourceList, | |
IN OUT UINT32 *ResourceSize | |
) | |
{ | |
if (*ResourceSize < mStmResourceSizeUsed) { | |
*ResourceSize = (UINT32)mStmResourceSizeUsed; | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
CopyMem (ResourceList, mStmResourcesPtr, mStmResourceSizeUsed); | |
*ResourceSize = (UINT32)mStmResourceSizeUsed; | |
return EFI_SUCCESS; | |
} | |
/** | |
Set valid bit for MSEG MSR. | |
@param Buffer Ap function buffer. (not used) | |
**/ | |
VOID | |
EFIAPI | |
EnableMsegMsr ( | |
IN VOID *Buffer | |
) | |
{ | |
MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; | |
SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); | |
SmmMonitorCtl.Bits.Valid = 1; | |
AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); | |
} | |
/** | |
Get 4K page aligned VMCS size. | |
@return 4K page aligned VMCS size | |
**/ | |
UINT32 | |
GetVmcsSize ( | |
VOID | |
) | |
{ | |
MSR_IA32_VMX_BASIC_REGISTER VmxBasic; | |
// | |
// Read VMCS size and and align to 4KB | |
// | |
VmxBasic.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_BASIC); | |
return ALIGN_VALUE (VmxBasic.Bits.VmcsSize, SIZE_4KB); | |
} | |
/** | |
Check STM image size. | |
@param StmImage STM image | |
@param StmImageSize STM image size | |
@retval TRUE check pass | |
@retval FALSE check fail | |
**/ | |
BOOLEAN | |
StmCheckStmImage ( | |
IN EFI_PHYSICAL_ADDRESS StmImage, | |
IN UINTN StmImageSize | |
) | |
{ | |
UINTN MinMsegSize; | |
STM_HEADER *StmHeader; | |
IA32_VMX_MISC_REGISTER VmxMiscMsr; | |
// | |
// Check to see if STM image is compatible with CPU | |
// | |
StmHeader = (STM_HEADER *)(UINTN)StmImage; | |
VmxMiscMsr.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_MISC); | |
if (StmHeader->HwStmHdr.MsegHeaderRevision != VmxMiscMsr.Bits.MsegRevisionIdentifier) { | |
DEBUG ((DEBUG_ERROR, "STM Image not compatible with CPU\n")); | |
DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.MsegHeaderRevision = %08x\n", StmHeader->HwStmHdr.MsegHeaderRevision)); | |
DEBUG ((DEBUG_ERROR, " VmxMiscMsr.Bits.MsegRevisionIdentifier = %08x\n", VmxMiscMsr.Bits.MsegRevisionIdentifier)); | |
return FALSE; | |
} | |
// | |
// Get Minimal required Mseg size | |
// | |
MinMsegSize = (EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (StmHeader->SwStmHdr.StaticImageSize)) + | |
StmHeader->SwStmHdr.AdditionalDynamicMemorySize + | |
(StmHeader->SwStmHdr.PerProcDynamicMemorySize + GetVmcsSize () * 2) * gSmst->NumberOfCpus); | |
if (MinMsegSize < StmImageSize) { | |
MinMsegSize = StmImageSize; | |
} | |
if (StmHeader->HwStmHdr.Cr3Offset >= StmHeader->SwStmHdr.StaticImageSize) { | |
// | |
// We will create page table, just in case that SINIT does not create it. | |
// | |
if (MinMsegSize < StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE (6)) { | |
MinMsegSize = StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE (6); | |
} | |
} | |
// | |
// Check if it exceeds MSEG size | |
// | |
if (MinMsegSize > mMsegSize) { | |
DEBUG ((DEBUG_ERROR, "MSEG too small. Min MSEG Size = %08x Current MSEG Size = %08x\n", MinMsegSize, mMsegSize)); | |
DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.StaticImageSize = %08x\n", StmHeader->SwStmHdr.StaticImageSize)); | |
DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.AdditionalDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.AdditionalDynamicMemorySize)); | |
DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.PerProcDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.PerProcDynamicMemorySize)); | |
DEBUG ((DEBUG_ERROR, " VMCS Size = %08x\n", GetVmcsSize ())); | |
DEBUG ((DEBUG_ERROR, " Max CPUs = %08x\n", gSmst->NumberOfCpus)); | |
DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.Cr3Offset = %08x\n", StmHeader->HwStmHdr.Cr3Offset)); | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Load STM image to MSEG. | |
@param StmImage STM image | |
@param StmImageSize STM image size | |
**/ | |
VOID | |
StmLoadStmImage ( | |
IN EFI_PHYSICAL_ADDRESS StmImage, | |
IN UINTN StmImageSize | |
) | |
{ | |
MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; | |
UINT32 MsegBase; | |
STM_HEADER *StmHeader; | |
// | |
// Get MSEG base address from MSR_IA32_SMM_MONITOR_CTL | |
// | |
SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); | |
MsegBase = SmmMonitorCtl.Bits.MsegBase << 12; | |
// | |
// Zero all of MSEG base address | |
// | |
ZeroMem ((VOID *)(UINTN)MsegBase, mMsegSize); | |
// | |
// Copy STM Image into MSEG | |
// | |
CopyMem ((VOID *)(UINTN)MsegBase, (VOID *)(UINTN)StmImage, StmImageSize); | |
// | |
// STM Header is at the beginning of the STM Image | |
// | |
StmHeader = (STM_HEADER *)(UINTN)StmImage; | |
StmGen4GPageTable ((UINTN)MsegBase + StmHeader->HwStmHdr.Cr3Offset); | |
} | |
/** | |
Load STM image to MSEG. | |
@param StmImage STM image | |
@param StmImageSize STM image size | |
@retval EFI_SUCCESS Load STM to MSEG successfully | |
@retval EFI_ALREADY_STARTED STM image is already loaded to MSEG | |
@retval EFI_BUFFER_TOO_SMALL MSEG is smaller than minimal requirement of STM image | |
@retval EFI_UNSUPPORTED MSEG is not enabled | |
**/ | |
EFI_STATUS | |
EFIAPI | |
LoadMonitor ( | |
IN EFI_PHYSICAL_ADDRESS StmImage, | |
IN UINTN StmImageSize | |
) | |
{ | |
MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; | |
if (mLockLoadMonitor) { | |
return EFI_ACCESS_DENIED; | |
} | |
SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); | |
if (SmmMonitorCtl.Bits.MsegBase == 0) { | |
return EFI_UNSUPPORTED; | |
} | |
if (!StmCheckStmImage (StmImage, StmImageSize)) { | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
// Record STM_HASH to PCR 0, just in case it is NOT TXT launch, we still need provide the evidence. | |
TpmMeasureAndLogData ( | |
0, // PcrIndex | |
TXT_EVTYPE_STM_HASH, // EventType | |
NULL, // EventLog | |
0, // LogLen | |
(VOID *)(UINTN)StmImage, // HashData | |
StmImageSize // HashDataLen | |
); | |
StmLoadStmImage (StmImage, StmImageSize); | |
mStmState |= EFI_SM_MONITOR_STATE_ENABLED; | |
return EFI_SUCCESS; | |
} | |
/** | |
This function return BIOS STM resource. | |
Produced by SmmStm. | |
Consumed by SmmMpService when Init. | |
@return BIOS STM resource | |
**/ | |
VOID * | |
GetStmResource ( | |
VOID | |
) | |
{ | |
return mStmResourcesPtr; | |
} | |
/** | |
This function notify STM resource change. | |
@param StmResource BIOS STM resource | |
**/ | |
VOID | |
NotifyStmResourceChange ( | |
VOID *StmResource | |
) | |
{ | |
UINTN Index; | |
TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; | |
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { | |
Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); | |
Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)StmResource; | |
} | |
return; | |
} | |
/** | |
This is STM setup BIOS callback. | |
**/ | |
VOID | |
EFIAPI | |
SmmStmSetup ( | |
VOID | |
) | |
{ | |
mStmState |= EFI_SM_MONITOR_STATE_ACTIVATED; | |
} | |
/** | |
This is STM teardown BIOS callback. | |
**/ | |
VOID | |
EFIAPI | |
SmmStmTeardown ( | |
VOID | |
) | |
{ | |
mStmState &= ~EFI_SM_MONITOR_STATE_ACTIVATED; | |
} |