summaryrefslogtreecommitdiffstats
path: root/UefiPayloadPkg/BlSupportSmm
diff options
context:
space:
mode:
Diffstat (limited to 'UefiPayloadPkg/BlSupportSmm')
-rw-r--r--UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c431
-rw-r--r--UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h41
-rw-r--r--UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf49
3 files changed, 521 insertions, 0 deletions
diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
new file mode 100644
index 0000000000..2283b6b4af
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.c
@@ -0,0 +1,431 @@
+/** @file
+ This driver is used for SMM S3 support for the bootloader that
+ doesn't support SMM.
+ The payload would save SMM rebase info to SMM communication area.
+ The bootloader is expected to rebase the SMM and trigger SMI by
+ writting 0xB2 port with given value from SMM communication area.
+ The paylaod SMM handler got chance to restore regs in S3 path.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <BlSupportSmm.h>
+
+PLD_S3_COMMUNICATION mPldS3Hob;
+EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *mSmramHob = NULL;
+PLD_SMM_REGISTERS *mSmmRegisterHob = NULL;;
+UINT64 mSmmFeatureControl = 0;
+
+/**
+ Save SMM rebase and SMI handler information to SMM communication area
+
+ The function detects SMM communication region for boot loader, if it is detected, it
+ will save SMM rebase information and S3 SMI handler information to SMM communication
+ region. Bootloader should consume these information in S3 path to restore smm base,
+ and write the 0xB2 port to trigger SMI so that payload could resume S3 registers.
+
+ @param[in] BlSwSmiHandlerInput Value written to 0xB2 to trigger SMI handler.
+
+ @retval EFI_SUCCESS Save SMM info success.
+ @retval Others Failed to save SMM info.
+**/
+EFI_STATUS
+SaveSmmInfoForS3 (
+ IN UINT8 BlSwSmiHandlerInput
+ )
+{
+ EFI_STATUS Status;
+ EFI_PROCESSOR_INFORMATION ProcessorInfo;
+ EFI_MP_SERVICES_PROTOCOL *MpService;
+ CPU_SMMBASE *SmmBaseInfo;
+ PLD_TO_BL_SMM_INFO *PldSmmInfo;
+ UINTN Index;
+
+ PldSmmInfo = (PLD_TO_BL_SMM_INFO *)(UINTN)mPldS3Hob.CommBuffer.PhysicalStart;
+ PldSmmInfo->Header.Header.HobLength = (UINT16)(sizeof (PLD_TO_BL_SMM_INFO) + gSmst->NumberOfCpus * sizeof (CPU_SMMBASE));
+ for (Index = 0; Index < mSmramHob->NumberOfSmmReservedRegions; Index++) {
+ if ((mPldS3Hob.CommBuffer.PhysicalStart >= mSmramHob->Descriptor[Index].PhysicalStart) &&
+ (mPldS3Hob.CommBuffer.PhysicalStart < mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
+ break;
+ }
+ }
+ if (Index == mSmramHob->NumberOfSmmReservedRegions) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Make sure the dedicated region for SMM info communication whose attribute is "allocated" (i.e., excluded from SMM memory service)
+ //
+ if ((mSmramHob->Descriptor[Index].RegionState & EFI_ALLOCATED) == 0) {
+ DEBUG ((DEBUG_ERROR, "SMM communication region not set to EFI_ALLOCATED\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength) > (mSmramHob->Descriptor[Index].PhysicalStart + mSmramHob->Descriptor[Index].PhysicalSize)) {
+ DEBUG ((DEBUG_ERROR, "SMM communication buffer (0x%x) is too small.\n", (UINTN)PldSmmInfo + PldSmmInfo->Header.Header.HobLength));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyGuid (&PldSmmInfo->Header.Name, &gS3CommunicationGuid);
+ PldSmmInfo->Header.Header.HobType = EFI_HOB_TYPE_GUID_EXTENSION;
+ PldSmmInfo->S3Info.SwSmiTriggerValue = BlSwSmiHandlerInput;
+
+ //
+ // Save APIC ID and SMM base
+ //
+ Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ PldSmmInfo->S3Info.CpuCount = (UINT32)gSmst->NumberOfCpus;
+ SmmBaseInfo = &PldSmmInfo->S3Info.SmmBase[0];
+ for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+ Status = MpService->GetProcessorInfo (MpService, Index, &ProcessorInfo);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ SmmBaseInfo->ApicId = (UINT32)(UINTN)ProcessorInfo.ProcessorId;
+ SmmBaseInfo->SmmBase = (UINT32)(UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET;
+ DEBUG ((DEBUG_INFO, "CPU%d ID:%02X Base: %08X\n", Index, SmmBaseInfo->ApicId, SmmBaseInfo->SmmBase));
+ SmmBaseInfo++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get specified SMI register based on given register ID
+
+ @param[in] Id The register ID to get.
+
+ @retval NULL The register is not found
+ @return smi register
+
+**/
+PLD_GENERIC_REGISTER *
+GetRegisterById (
+ UINT64 Id
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < mSmmRegisterHob->Count; Index++) {
+ if (mSmmRegisterHob->Registers[Index].Id == Id) {
+ return &mSmmRegisterHob->Registers[Index];
+ }
+ }
+ return NULL;
+}
+
+/**
+ Set SMM SMI Global enable lock
+
+**/
+VOID
+LockSmiGlobalEn (
+ VOID
+ )
+{
+ PLD_GENERIC_REGISTER *SmiLockReg;
+
+ DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn .....\n"));
+
+ SmiLockReg = GetRegisterById (REGISTER_ID_SMI_GBL_EN_LOCK);
+ if (SmiLockReg == NULL) {
+ DEBUG ((DEBUG_ERROR, "SMI global enable lock reg not found.\n"));
+ return;
+ }
+
+ //
+ // Set SMM SMI lock in S3 path
+ //
+ if ((SmiLockReg->Address.AccessSize == EFI_ACPI_3_0_DWORD) &&
+ (SmiLockReg->Address.Address != 0) &&
+ (SmiLockReg->Address.RegisterBitWidth == 1) &&
+ (SmiLockReg->Address.AddressSpaceId == EFI_ACPI_3_0_SYSTEM_MEMORY) &&
+ (SmiLockReg->Value == 1)) {
+ DEBUG ((DEBUG_ERROR, "LockSmiGlobalEn ....is locked\n"));
+
+ MmioOr32 ((UINT32)SmiLockReg->Address.Address, 1 << SmiLockReg->Address.RegisterBitOffset);
+ } else {
+ DEBUG ((DEBUG_ERROR, "Unexpected SMM SMI lock register, need enhancement here.\n"));
+ }
+}
+
+/**
+ Check and set SMM feature lock bit and code check enable bit
+ in S3 path.
+
+**/
+VOID
+SmmFeatureLockOnS3 (
+ VOID
+ )
+{
+
+ if (mSmmFeatureControl != 0) {
+ return;
+ }
+
+ mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
+ if ((mSmmFeatureControl & 0x5) != 0x5) {
+ //
+ // Set Lock bit [BIT0] for this register and SMM code check enable bit [BIT2]
+ //
+ AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, mSmmFeatureControl | 0x5);
+ }
+ mSmmFeatureControl = AsmReadMsr64(MSR_SMM_FEATURE_CONTROL);
+}
+
+
+
+/**
+ Function to program SMRR base and mask.
+
+ @param[in] ProcedureArgument Pointer to SMRR_BASE_MASK structure.
+**/
+VOID
+SetSmrr (
+ IN VOID *ProcedureArgument
+ )
+{
+ if (ProcedureArgument != NULL) {
+ AsmWriteMsr64 (MSR_IA32_SMRR_PHYSBASE, ((SMRR_BASE_MASK *)ProcedureArgument)->Base);
+ AsmWriteMsr64 (MSR_IA32_SMRR_PHYSMASK, ((SMRR_BASE_MASK *)ProcedureArgument)->Mask);
+ }
+}
+
+/**
+ Set SMRR in S3 path.
+
+**/
+VOID
+SetSmrrOnS3 (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ SMRR_BASE_MASK Arguments;
+ UINTN Index;
+ UINT32 SmmBase;
+ UINT32 SmmSize;
+
+ if ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSBASE) != 0) && ((AsmReadMsr64 (MSR_IA32_SMRR_PHYSMASK) & BIT11) != 0)) {
+ return;
+ }
+
+ SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalStart;
+ SmmSize = (UINT32)(UINTN)mSmramHob->Descriptor[0].PhysicalSize;
+ if ((mSmramHob->NumberOfSmmReservedRegions > 2) || (mSmramHob->NumberOfSmmReservedRegions == 0)) {
+ DEBUG ((DEBUG_ERROR, "%d SMM ranges are not supported.\n", mSmramHob->NumberOfSmmReservedRegions));
+ return;
+ } else if (mSmramHob->NumberOfSmmReservedRegions == 2) {
+ if ((mSmramHob->Descriptor[1].PhysicalStart + mSmramHob->Descriptor[1].PhysicalSize) == SmmBase){
+ SmmBase = (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalStart;
+ } else if (mSmramHob->Descriptor[1].PhysicalStart != (SmmBase + SmmSize)) {
+ DEBUG ((DEBUG_ERROR, "Two SMM regions are not continous.\n"));
+ return;
+ }
+ SmmSize += (UINT32)(UINTN)mSmramHob->Descriptor[1].PhysicalSize;
+ }
+
+ if ((SmmBase == 0) || (SmmSize < SIZE_4KB)) {
+ DEBUG ((DEBUG_ERROR, "Invalid SMM range.\n"));
+ return ;
+ }
+
+ //
+ // SMRR size must be of length 2^n
+ // SMRR base alignment cannot be less than SMRR length
+ //
+ if ((SmmSize != GetPowerOfTwo32 (SmmSize)) || ((SmmBase & ~(SmmSize - 1)) != SmmBase)) {
+ DEBUG ((DEBUG_ERROR, " Invalid SMM range.\n"));
+ return ;
+ }
+
+ //
+ // Calculate smrr base, mask and pass them as arguments.
+ //
+ Arguments.Base = (SmmSize | MTRR_CACHE_WRITE_BACK);
+ Arguments.Mask = (~(SmmSize - 1) & EFI_MSR_SMRR_MASK);
+
+ //
+ // Set SMRR valid bit
+ //
+ Arguments.Mask |= BIT11;
+
+ //
+ // Program smrr base and mask on BSP first and then on APs
+ //
+ SetSmrr(&Arguments);
+ for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+ if (Index != gSmst->CurrentlyExecutingCpu) {
+ Status = gSmst->SmmStartupThisAp (SetSmrr, Index, (VOID *)&Arguments);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "Programming SMRR on AP# %d status: %r\n", Index, Status));
+ }
+ }
+ }
+}
+
+
+/**
+ Software SMI callback for restoring SMRR base and mask in S3 path.
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param[in] Context Points to an optional handler context which was specified when the
+ handler was registered.
+ @param[in, out] CommBuffer A pointer to a collection of data in memory that will
+ be conveyed from a non-SMM environment into an SMM environment.
+ @param[in, out] CommBufferSize The size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+BlSwSmiHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *Context,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ SetSmrrOnS3 ();
+ SmmFeatureLockOnS3 ();
+ LockSmiGlobalEn ();
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Lock SMI in this SMM ready to lock event.
+
+ @param Protocol Points to the protocol's unique identifier
+ @param Interface Points to the interface instance
+ @param Handle The handle on which the interface was installed
+
+ @retval EFI_SUCCESS SmmEventCallback runs successfully
+ @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
+ **/
+EFI_STATUS
+EFIAPI
+BlSupportSmmReadyToLockCallback (
+ IN CONST EFI_GUID *Protocol,
+ IN VOID *Interface,
+ IN EFI_HANDLE Handle
+ )
+{
+ //
+ // Set SMM SMI lock
+ //
+ LockSmiGlobalEn ();
+ return EFI_SUCCESS;
+}
+
+
+/**
+ The driver's entry point.
+
+ @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 entry point is executed successfully.
+ @retval Others Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+BlSupportSmm (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
+ EFI_SMM_SW_REGISTER_CONTEXT SwContext;
+ EFI_HANDLE SwHandle;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ VOID *SmmHob;
+ VOID *Registration;
+
+ //
+ // Get SMM S3 communication hob and save it
+ //
+ GuidHob = GetFirstGuidHob (&gS3CommunicationGuid);
+ if (GuidHob != NULL) {
+ SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+ CopyMem (&mPldS3Hob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ if (mPldS3Hob.PldAcpiS3Enable) {
+ // Other drivers will take care of S3.
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Get smram hob and save it
+ //
+ GuidHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
+ if (GuidHob != NULL) {
+ SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+ mSmramHob = AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob));
+ if (mSmramHob == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (mSmramHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get SMM register hob and save it
+ //
+ GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
+ if (GuidHob != NULL) {
+ SmmHob = (VOID *) (GET_GUID_HOB_DATA(GuidHob));
+ mSmmRegisterHob = AllocatePool (GET_GUID_HOB_DATA_SIZE(GuidHob));
+ if (mSmmRegisterHob == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (mSmmRegisterHob, SmmHob, GET_GUID_HOB_DATA_SIZE(GuidHob));
+ } else {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get the Sw dispatch protocol and register SMI handler.
+ //
+ Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**)&SwDispatch);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ SwContext.SwSmiInputValue = (UINTN) -1;
+ Status = SwDispatch->Register (SwDispatch, BlSwSmiHandler, &SwContext, &SwHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Registering S3 smi handler failed: %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Register SMM ready to lock callback
+ //
+ Status = gSmst->SmmRegisterProtocolNotify (
+ &gEfiSmmReadyToLockProtocolGuid,
+ BlSupportSmmReadyToLockCallback,
+ &Registration
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ SaveSmmInfoForS3 ((UINT8)SwContext.SwSmiInputValue);
+
+ return EFI_SUCCESS;
+}
+
diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
new file mode 100644
index 0000000000..f4386cb842
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.h
@@ -0,0 +1,41 @@
+/** @file
+ The header file of bootloader support SMM.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef BL_SUPPORT_SMM_H_
+#define BL_SUPPORT_SMM_H_
+
+#include <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/HobLib.h>
+#include <Library/MtrrLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/PciLib.h>
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmAccess2.h>
+#include <protocol/MpService.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Register/Intel/ArchitecturalMsr.h>
+#include <Guid/SmmRegisterInfoGuid.h>
+#include <Guid/SmmS3CommunicationInfoGuid.h>
+#include <Guid/SmramMemoryReserve.h>
+
+#define EFI_MSR_SMRR_MASK 0xFFFFF000
+#define MSR_SMM_FEATURE_CONTROL 0x4E0
+#define SMRAM_SAVE_STATE_MAP_OFFSET 0xFC00 /// Save state offset from SMBASE
+
+typedef struct {
+ UINT32 Base;
+ UINT32 Mask;
+} SMRR_BASE_MASK;
+
+#endif
+
diff --git a/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
new file mode 100644
index 0000000000..75d4777971
--- /dev/null
+++ b/UefiPayloadPkg/BlSupportSmm/BlSupportSmm.inf
@@ -0,0 +1,49 @@
+## @file
+# Bootloader Support SMM module
+#
+# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = BlSupportSmm
+ FILE_GUID = AA292DE7-E11E-42E6-846B-5813A5A8D982
+ MODULE_TYPE = DXE_SMM_DRIVER
+ PI_SPECIFICATION_VERSION = 0x0001000A
+ VERSION_STRING = 1.0
+ ENTRY_POINT = BlSupportSmm
+
+[Sources]
+ BlSupportSmm.c
+ BlSupportSmm.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ UefiPayloadPkg/UefiPayloadPkg.dec
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ SmmServicesTableLib
+ MemoryAllocationLib
+ BaseLib
+ IoLib
+ HobLib
+
+[Guids]
+ gS3CommunicationGuid
+ gEfiSmmSmramMemoryGuid
+ gSmmRegisterInfoGuid
+
+[Protocols]
+ gEfiSmmSwDispatch2ProtocolGuid
+ gEfiMpServiceProtocolGuid
+ gEfiSmmReadyToLockProtocolGuid
+
+[Depex]
+ gEfiSmmSwDispatch2ProtocolGuid