summaryrefslogtreecommitdiff
path: root/Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c')
-rw-r--r--Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c1299
1 files changed, 1299 insertions, 0 deletions
diff --git a/Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c b/Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c
new file mode 100644
index 0000000000..03937dcbe2
--- /dev/null
+++ b/Core/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c
@@ -0,0 +1,1299 @@
+/** @file
+ SMM STM support functions
+
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiSmm.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/Cpuid.h>
+#include <Register/ArchitecturalMsr.h>
+#include <Register/SmramSaveStateMap.h>
+
+#include <Protocol/MpService.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
+
+/**
+ The constructor function
+
+ @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
+SmmCpuFeaturesLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+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
+
+ @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;
+
+ //
+ // Call the common constructor function
+ //
+ Status = SmmCpuFeaturesLibConstructor (ImageHandle, SystemTable);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // 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_ERROR, "CpuSmmStmExceptionStackSize - %x\n", PcdGet32(PcdCpuSmmStmExceptionStackSize)));
+ DEBUG((DEBUG_ERROR, "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_ERROR, "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_ERROR, "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 intialization
+ //
+ 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.
+ Comsumed 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;
+}
+