--- /dev/null
+/** @file\r
+ This module produces the SMM Control2 Protocol\r
+\r
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+#include <Protocol/SmmControl2.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Guid/SmmRegisterInfoGuid.h>\r
+\r
+#define SMM_DATA_PORT 0xB3\r
+#define SMM_CONTROL_PORT 0xB2\r
+\r
+typedef struct {\r
+ UINT8 GblBitOffset;\r
+ UINT8 ApmBitOffset;\r
+ UINT32 Address;\r
+} SMM_CONTROL2_REG;\r
+\r
+SMM_CONTROL2_REG mSmiCtrlReg;\r
+\r
+/**\r
+ Invokes SMI activation from either the preboot or runtime environment.\r
+\r
+ This function generates an SMI.\r
+\r
+ @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.\r
+ @param[in,out] CommandPort The value written to the command port.\r
+ @param[in,out] DataPort The value written to the data port.\r
+ @param[in] Periodic Optional mechanism to engender a periodic stream.\r
+ @param[in] ActivationInterval Optional parameter to repeat at this period one\r
+ time or, if the Periodic Boolean is set, periodically.\r
+\r
+ @retval EFI_SUCCESS The SMI has been engendered.\r
+ @retval EFI_DEVICE_ERROR The timing is unsupported.\r
+ @retval EFI_INVALID_PARAMETER The activation period is unsupported.\r
+ @retval EFI_INVALID_PARAMETER The last periodic activation has not been cleared.\r
+ @retval EFI_NOT_STARTED The MM base service has not been initialized.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Activate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN OUT UINT8 *CommandPort OPTIONAL,\r
+ IN OUT UINT8 *DataPort OPTIONAL,\r
+ IN BOOLEAN Periodic OPTIONAL,\r
+ IN EFI_SMM_PERIOD ActivationInterval OPTIONAL\r
+ )\r
+{\r
+ UINT32 SmiEn;\r
+ UINT32 SmiEnableBits;\r
+\r
+ if (Periodic) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ SmiEn = IoRead32 (mSmiCtrlReg.Address);\r
+ SmiEnableBits = (1 << mSmiCtrlReg.GblBitOffset) | (1 << mSmiCtrlReg.ApmBitOffset);\r
+ if ((SmiEn & SmiEnableBits) != SmiEnableBits) {\r
+ //\r
+ // Set the "global SMI enable" bit and APM bit\r
+ //\r
+ IoWrite32 (mSmiCtrlReg.Address, SmiEn | SmiEnableBits);\r
+ }\r
+\r
+ IoWrite8 (SMM_DATA_PORT, DataPort == NULL ? 0 : *DataPort);\r
+ IoWrite8 (SMM_CONTROL_PORT, CommandPort == NULL ? 0 : *CommandPort);\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Clears an SMI.\r
+\r
+ @param This Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL\r
+ @param Periodic TRUE to indicate a periodical SMI\r
+\r
+ @return Return value from SmmClear ()\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Deactivate (\r
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,\r
+ IN BOOLEAN Periodic\r
+ )\r
+{\r
+ if (Periodic) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Temporarily do nothing here\r
+ //\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+///\r
+/// SMM COntrol2 Protocol instance\r
+///\r
+EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {\r
+ Activate,\r
+ Deactivate,\r
+ 0\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
+ return PldReg;\r
+}\r
+\r
+\r
+/**\r
+ Fixup data pointers so that the services can be called in virtual mode.\r
+\r
+ @param[in] Event The event registered.\r
+ @param[in] Context Event context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SmmControlVirtualAddressChangeEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EfiConvertPointer (0x0, (VOID **) &(mSmmControl2.Trigger));\r
+ EfiConvertPointer (0x0, (VOID **) &(mSmmControl2.Clear));\r
+}\r
+\r
+\r
+/**\r
+ This function installs EFI_SMM_CONTROL2_PROTOCOL.\r
+\r
+ @param ImageHandle Handle for the image of this driver\r
+ @param SystemTable Pointer to the EFI System Table\r
+\r
+ @retval EFI_UNSUPPORTED There's no Intel ICH on this platform\r
+ @return The status returned from InstallProtocolInterface().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmControlEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HOB_GUID_TYPE *GuidHob;\r
+ PLD_SMM_REGISTERS *SmmRegister;\r
+ PLD_GENERIC_REGISTER *SmiGblEnReg;\r
+ PLD_GENERIC_REGISTER *SmiApmEnReg;\r
+ EFI_EVENT Event;\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
+ SmiGblEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_GBL_EN);\r
+ if (SmiGblEnReg == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "SMI global enable reg not found.\n"));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ mSmiCtrlReg.Address = (UINT32)SmiGblEnReg->Address.Address;\r
+ mSmiCtrlReg.GblBitOffset = SmiGblEnReg->Address.RegisterBitOffset;\r
+\r
+ SmiApmEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_EN);\r
+ if (SmiApmEnReg == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "SMI APM enable reg not found.\n"));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (SmiApmEnReg->Address.Address != mSmiCtrlReg.Address) {\r
+ DEBUG ((DEBUG_ERROR, "SMI APM EN and SMI GBL EN are expected to have same register base\n"));\r
+ DEBUG ((DEBUG_ERROR, "APM:0x%x, GBL:0x%x\n", SmiApmEnReg->Address.Address, mSmiCtrlReg.Address));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ mSmiCtrlReg.ApmBitOffset = SmiApmEnReg->Address.RegisterBitOffset;\r
+\r
+ //\r
+ // Install our protocol interfaces on the device's handle\r
+ //\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &ImageHandle,\r
+ &gEfiSmmControl2ProtocolGuid,\r
+ &mSmmControl2,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ SmmControlVirtualAddressChangeEvent,\r
+ NULL,\r
+ &gEfiEventVirtualAddressChangeGuid,\r
+ &Event\r
+ );\r
+ return Status;\r
+}\r