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