diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c | 1281 |
1 files changed, 1281 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c new file mode 100644 index 00000000..6ae8fb92 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c @@ -0,0 +1,1281 @@ +/** @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; +} + |