diff options
Diffstat (limited to 'UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c')
-rw-r--r-- | UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c new file mode 100644 index 0000000000..5e9ae0db7c --- /dev/null +++ b/UefiPayloadPkg/PchSmiDispatchSmm/PchSmiDispatchSmm.c @@ -0,0 +1,455 @@ +/** @file
+ SMM SwDispatch2 Protocol.
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+
+
+**/
+
+#include "PchSmiDispatchSmm.h"
+
+typedef struct {
+ UINT8 EosBitOffset;
+ UINT8 ApmBitOffset;
+ UINT32 SmiEosAddr;
+ UINT32 SmiApmStsAddr;
+} SMM_PCH_REGISTER;
+
+SMM_PCH_REGISTER mSmiPchReg;
+
+EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol;
+LIST_ENTRY mSmmSwDispatch2Queue = INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2Queue);
+
+/**
+ Find SmmSwDispatch2Context by SwSmiInputValue.
+
+ @param[in] SwSmiInputValue The value to indentify the SmmSwDispatch2 context
+
+ @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
+**/
+EFI_SMM_SW_DISPATCH2_CONTEXT *
+FindContextBySwSmiInputValue (
+ IN UINTN SwSmiInputValue
+ )
+{
+ LIST_ENTRY *Node;
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
+
+ Node = mSmmSwDispatch2Queue.ForwardLink;
+ for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+ Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+ if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
+ return Dispatch2Context;
+ }
+ }
+ return NULL;
+}
+
+/**
+ Find SmmSwDispatch2Context by DispatchHandle.
+
+ @param DispatchHandle The handle to indentify the SmmSwDispatch2 context
+
+ @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
+**/
+EFI_SMM_SW_DISPATCH2_CONTEXT *
+FindContextByDispatchHandle (
+ IN EFI_HANDLE DispatchHandle
+ )
+{
+ LIST_ENTRY *Node;
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
+
+ Node = mSmmSwDispatch2Queue.ForwardLink;
+ for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+ Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+ if (Dispatch2Context->DispatchHandle == DispatchHandle) {
+ return Dispatch2Context;
+ }
+ }
+ return NULL;
+}
+
+/**
+ Dispatch registered SMM handlers
+
+ @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param RegisterContext Points to an optional handler context which was specified when the handler was registered.
+ @param CommBuffer A pointer to a collection of data in memory that will
+ be conveyed from a non-SMM environment into an SMM environment.
+ @param CommBufferSize The size of the CommBuffer.
+
+ @return Status Code
+
+**/
+EFI_STATUS
+SmmSwDispatcher (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ EFI_STATUS Status;
+ EFI_SMM_SW_CONTEXT SwContext;
+ UINTN Index;
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
+ EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction;
+ EFI_SMM_SW_REGISTER_CONTEXT DispatchContext;
+ UINTN Size;
+ EFI_SMM_SAVE_STATE_IO_INFO IoInfo;
+
+ //
+ // Construct new context
+ //
+ SwContext.SwSmiCpuIndex = 0;
+ SwContext.CommandPort = IoRead8 (SMM_CONTROL_PORT);
+ SwContext.DataPort = IoRead8 (SMM_DATA_PORT);
+
+ //
+ // Try to find which CPU trigger SWSMI
+ //
+ for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
+ Status = mSmmCpuProtocol->ReadSaveState (
+ mSmmCpuProtocol,
+ sizeof(IoInfo),
+ EFI_SMM_SAVE_STATE_REGISTER_IO,
+ Index,
+ &IoInfo
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ if (IoInfo.IoPort == SMM_CONTROL_PORT) {
+ //
+ // Great! Find it.
+ //
+ SwContext.SwSmiCpuIndex = Index;
+ DEBUG ((DEBUG_VERBOSE, "CPU index = 0x%x/0x%x\n", Index, gSmst->NumberOfCpus));
+ break;
+ }
+ }
+
+ if (SwContext.CommandPort == 0) {
+ DEBUG ((DEBUG_VERBOSE, "NOT SW SMI\n"));
+ Status = EFI_SUCCESS;
+ goto End;
+ }
+
+ //
+ // Search context
+ //
+ Context = FindContextBySwSmiInputValue (SwContext.CommandPort);
+ if (Context == NULL) {
+ DEBUG ((DEBUG_INFO, "No handler for SMI value 0x%x\n", SwContext.CommandPort));
+ Status = EFI_SUCCESS;
+ goto End;
+ }
+ DEBUG ((DEBUG_VERBOSE, "Prepare to call handler for 0x%x\n", SwContext.CommandPort));
+
+ //
+ // Dispatch
+ //
+ DispatchContext.SwSmiInputValue = SwContext.CommandPort;
+ Size = sizeof(SwContext);
+ DispatchFunction = (EFI_SMM_HANDLER_ENTRY_POINT2)Context->DispatchFunction;
+ Status = DispatchFunction (DispatchHandle, &DispatchContext, &SwContext, &Size);
+
+End:
+ //
+ // Clear SMI APM status
+ //
+ IoOr32 (mSmiPchReg.SmiApmStsAddr, 1 << mSmiPchReg.ApmBitOffset);
+
+
+ //
+ // Set EOS bit
+ //
+ IoOr32 (mSmiPchReg.SmiEosAddr, 1 << mSmiPchReg.EosBitOffset);
+
+ return Status;
+}
+
+
+/**
+Check the SwSmiInputValue is already used
+
+@param[in] SwSmiInputValue To indentify the SmmSwDispatch2 context
+
+@retval EFI_SUCCESS SwSmiInputValue could be used.
+@retval EFI_INVALID_PARAMETER SwSmiInputValue is already be used.
+
+**/
+EFI_STATUS
+SmiInputValueCheck (
+ IN UINTN SwSmiInputValue
+ )
+{
+ LIST_ENTRY *Node;
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
+
+ Node = mSmmSwDispatch2Queue.ForwardLink;
+ for (; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
+ Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
+ if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Register a child SMI source dispatch function for the specified software SMI.
+
+ This service registers a function (DispatchFunction) which will be called when the software
+ SMI source specified by RegContext->SwSmiCpuIndex is detected. On return, DispatchHandle
+ contains a unique handle which may be used later to unregister the function using UnRegister().
+
+ @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
+ @param[in] DispatchFunction Function to register for handler when the specified software
+ SMI is generated.
+ @param[in, out] RegContext Pointer to the dispatch function's context.
+ The caller fills this context in before calling
+ the register function to indicate to the register
+ function which Software SMI input value the
+ dispatch function should be invoked for.
+ @param[out] DispatchHandle Handle generated by the dispatcher to track the
+ function instance.
+
+ @retval EFI_SUCCESS The dispatch function has been successfully
+ registered and the SMI source has been enabled.
+ @retval EFI_DEVICE_ERROR The SW driver was unable to enable the SMI source.
+ @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The SW SMI input value
+ is not within valid range.
+ @retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this
+ child.
+ @retval EFI_OUT_OF_RESOURCES A unique software SMI value could not be assigned
+ for this dispatch.
+**/
+EFI_STATUS
+EFIAPI
+SmmSwDispatch2Register (
+ IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
+ IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
+ IN OUT EFI_SMM_SW_REGISTER_CONTEXT *RegContext,
+ OUT EFI_HANDLE *DispatchHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
+
+ if (RegContext->SwSmiInputValue == (UINTN)-1) {
+ //
+ // If SwSmiInputValue is set to (UINTN) -1 then a unique value
+ // will be assigned and returned in the structure.
+ //
+ Status = EFI_NOT_FOUND;
+ for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
+ Status = SmiInputValueCheck (Index);
+ if (!EFI_ERROR (Status)) {
+ RegContext->SwSmiInputValue = Index;
+ break;
+ }
+ }
+ if (RegContext->SwSmiInputValue == (UINTN)-1) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ if ((RegContext->SwSmiInputValue > MAXIMUM_SWI_VALUE) || (RegContext->SwSmiInputValue == 0)) {
+ DEBUG ((DEBUG_ERROR, "ERROR: SMI value range (1 ~ 0x%x)\n", MAXIMUM_SWI_VALUE));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Register
+ //
+ Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof(*Context), (VOID **)&Context);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *DispatchHandle = (EFI_HANDLE )Context;
+ Context->Signature = SMI_SW_HANDLER_SIGNATURE;
+ Context->SwSmiInputValue = RegContext->SwSmiInputValue;
+ Context->DispatchFunction = (UINTN)DispatchFunction;
+ Context->DispatchHandle = *DispatchHandle;
+ InsertTailList (&mSmmSwDispatch2Queue, &Context->Link);
+
+ return Status;
+}
+
+
+/**
+ Unregister a child SMI source dispatch function for the specified software SMI.
+
+ This service removes the handler associated with DispatchHandle so that it will no longer be
+ called in response to a software SMI.
+
+ @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
+ @param[in] DispatchHandle Handle of dispatch function to deregister.
+
+ @retval EFI_SUCCESS The dispatch function has been successfully unregistered.
+ @retval EFI_INVALID_PARAMETER The DispatchHandle was not valid.
+**/
+EFI_STATUS
+EFIAPI
+SmmSwDispatch2UnRegister (
+ IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
+ IN EFI_HANDLE DispatchHandle
+ )
+{
+ EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
+
+ //
+ // Unregister
+ //
+ Context = FindContextByDispatchHandle (DispatchHandle);
+ ASSERT (Context != NULL);
+ if (Context != NULL) {
+ RemoveEntryList (&Context->Link);
+ gSmst->SmmFreePool (Context);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2 = {
+ SmmSwDispatch2Register,
+ SmmSwDispatch2UnRegister,
+ MAXIMUM_SWI_VALUE
+};
+
+
+/**
+ Get specified SMI register based on given register ID
+
+ @param[in] SmmRegister SMI related register array from bootloader
+ @param[in] Id The register ID to get.
+
+ @retval NULL The register is not found or the format is not expected.
+ @return smi register
+
+**/
+PLD_GENERIC_REGISTER *
+GetSmmCtrlRegById (
+ IN PLD_SMM_REGISTERS *SmmRegister,
+ IN UINT32 Id
+ )
+{
+ UINT32 Index;
+ PLD_GENERIC_REGISTER *PldReg;
+
+ PldReg = NULL;
+ for (Index = 0; Index < SmmRegister->Count; Index++) {
+ if (SmmRegister->Registers[Index].Id == Id) {
+ PldReg = &SmmRegister->Registers[Index];
+ break;
+ }
+ }
+
+ if (PldReg == NULL) {
+ DEBUG ((DEBUG_INFO, "Register %d not found.\n", Id));
+ return NULL;
+ }
+
+ //
+ // Checking the register if it is expected.
+ //
+ if ((PldReg->Address.AccessSize != EFI_ACPI_3_0_DWORD) ||
+ (PldReg->Address.Address == 0) ||
+ (PldReg->Address.RegisterBitWidth != 1) ||
+ (PldReg->Address.AddressSpaceId != EFI_ACPI_3_0_SYSTEM_IO) ||
+ (PldReg->Value != 1)) {
+ DEBUG ((DEBUG_INFO, "Unexpected SMM register.\n"));
+ DEBUG ((DEBUG_INFO, "AddressSpaceId= 0x%x\n", PldReg->Address.AddressSpaceId));
+ DEBUG ((DEBUG_INFO, "RegBitWidth = 0x%x\n", PldReg->Address.RegisterBitWidth));
+ DEBUG ((DEBUG_INFO, "RegBitOffset = 0x%x\n", PldReg->Address.RegisterBitOffset));
+ DEBUG ((DEBUG_INFO, "AccessSize = 0x%x\n", PldReg->Address.AccessSize));
+ DEBUG ((DEBUG_INFO, "Address = 0x%lx\n",PldReg->Address.Address ));
+ return NULL;
+ }
+
+ return PldReg;
+}
+
+
+/**
+ Entry Point for this driver.
+
+ @param[in] ImageHandle Image handle of this driver.
+ @param[in] SystemTable A Pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurred when executing this entry point.
+**/
+EFI_STATUS
+EFIAPI
+PchSmiDispatchEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE DispatchHandle;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ PLD_SMM_REGISTERS *SmmRegister;
+ PLD_GENERIC_REGISTER *SmiEosReg;
+ PLD_GENERIC_REGISTER *SmiApmStsReg;
+
+ GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
+ if (GuidHob == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ SmmRegister = (PLD_SMM_REGISTERS *) GET_GUID_HOB_DATA(GuidHob);
+ SmiEosReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_EOS);
+ if (SmiEosReg == NULL) {
+ DEBUG ((DEBUG_ERROR, "SMI EOS reg not found.\n"));
+ return EFI_NOT_FOUND;
+ }
+ mSmiPchReg.SmiEosAddr = (UINT32)SmiEosReg->Address.Address;
+ mSmiPchReg.EosBitOffset = SmiEosReg->Address.RegisterBitOffset;
+
+ SmiApmStsReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_STS);
+ if (SmiApmStsReg == NULL) {
+ DEBUG ((DEBUG_ERROR, "SMI APM status reg not found.\n"));
+ return EFI_NOT_FOUND;
+ }
+ mSmiPchReg.SmiApmStsAddr = (UINT32)SmiApmStsReg->Address.Address;
+ mSmiPchReg.ApmBitOffset = SmiApmStsReg->Address.RegisterBitOffset;
+
+ //
+ // Locate PI SMM CPU protocol
+ //
+ Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Register a SMM handler to handle subsequent SW SMIs.
+ //
+ Status = gSmst->SmiHandlerRegister ((EFI_MM_HANDLER_ENTRY_POINT)SmmSwDispatcher, NULL, &DispatchHandle);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Publish PI SMM SwDispatch2 Protocol
+ //
+ ImageHandle = NULL;
+ Status = gSmst->SmmInstallProtocolInterface (
+ &ImageHandle,
+ &gEfiSmmSwDispatch2ProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gSmmSwDispatch2
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
|